add ExpungeSolo option
REFMAIL: CAOgBZNonT0s0b_yPs2vx81Ru3cQp5M93xpZ3syWBW-2CNoX_ow@mail.gmail.com
This commit is contained in:
parent
95a22739fa
commit
1225f0b86b
2
NEWS
2
NEWS
|
@ -20,6 +20,8 @@ A proper summary is now printed prior to exiting.
|
||||||
|
|
||||||
Added new sync operation 'Old'.
|
Added new sync operation 'Old'.
|
||||||
|
|
||||||
|
Added support for mirroring deletions more accurately.
|
||||||
|
|
||||||
[1.4.0]
|
[1.4.0]
|
||||||
|
|
||||||
The 'isync' compatibility wrapper was removed.
|
The 'isync' compatibility wrapper was removed.
|
||||||
|
|
|
@ -174,6 +174,7 @@ static const struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
} boxOps[] = {
|
} boxOps[] = {
|
||||||
{ OP_EXPUNGE, "Expunge" },
|
{ OP_EXPUNGE, "Expunge" },
|
||||||
|
{ OP_EXPUNGE_SOLO, "ExpungeSolo" },
|
||||||
{ OP_CREATE, "Create" },
|
{ OP_CREATE, "Create" },
|
||||||
{ OP_REMOVE, "Remove" },
|
{ OP_REMOVE, "Remove" },
|
||||||
};
|
};
|
||||||
|
|
|
@ -54,6 +54,7 @@ flag_str_t ATTR_OPTIMIZE /* force RVO */ fmt_lone_flags( uchar flags );
|
||||||
BIT_ENUM(
|
BIT_ENUM(
|
||||||
M_RECENT, // unsyncable flag; maildir_*() depend on this being bit 0
|
M_RECENT, // unsyncable flag; maildir_*() depend on this being bit 0
|
||||||
M_DEAD, // expunged
|
M_DEAD, // expunged
|
||||||
|
M_EXPUNGE, // for driver_t->close_box()
|
||||||
M_FLAGS, // flags are valid
|
M_FLAGS, // flags are valid
|
||||||
// The following are only for IMAP FETCH response parsing
|
// The following are only for IMAP FETCH response parsing
|
||||||
M_DATE,
|
M_DATE,
|
||||||
|
|
|
@ -3122,7 +3122,7 @@ imap_close_box( store_t *gctx,
|
||||||
|
|
||||||
for (msg = ctx->msgs.head; ; ) {
|
for (msg = ctx->msgs.head; ; ) {
|
||||||
for (bl = 0; msg && bl < 960; msg = msg->next) {
|
for (bl = 0; msg && bl < 960; msg = msg->next) {
|
||||||
if ((msg->status & M_DEAD) || !(msg->flags & F_DELETED))
|
if ((msg->status & M_DEAD) || !(msg->status & M_EXPUNGE))
|
||||||
continue;
|
continue;
|
||||||
if (bl)
|
if (bl)
|
||||||
buf[bl++] = ',';
|
buf[bl++] = ',';
|
||||||
|
@ -3136,7 +3136,7 @@ imap_close_box( store_t *gctx,
|
||||||
} else {
|
} else {
|
||||||
if (nmsg->seq > 1)
|
if (nmsg->seq > 1)
|
||||||
break;
|
break;
|
||||||
if (!(nmsg->flags & F_DELETED))
|
if (!(nmsg->flags & M_EXPUNGE))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1806,7 +1806,7 @@ maildir_close_box( store_t *gctx,
|
||||||
retry = 0;
|
retry = 0;
|
||||||
basel = nfsnprintf( buf, sizeof(buf), "%s/", ctx->path );
|
basel = nfsnprintf( buf, sizeof(buf), "%s/", ctx->path );
|
||||||
for (msg = ctx->msgs; msg; msg = msg->next) {
|
for (msg = ctx->msgs; msg; msg = msg->next) {
|
||||||
if (!(msg->status & M_DEAD) && (msg->flags & F_DELETED)) {
|
if (!(msg->status & M_DEAD) && (msg->status & M_EXPUNGE)) {
|
||||||
nfsnprintf( buf + basel, _POSIX_PATH_MAX - basel, "%s/%s", subdirs[msg->status & M_RECENT], msg->base );
|
nfsnprintf( buf + basel, _POSIX_PATH_MAX - basel, "%s/%s", subdirs[msg->status & M_RECENT], msg->base );
|
||||||
if (unlink( buf )) {
|
if (unlink( buf )) {
|
||||||
if (errno == ENOENT)
|
if (errno == ENOENT)
|
||||||
|
|
17
src/main.c
17
src/main.c
|
@ -43,6 +43,7 @@ PACKAGE " " VERSION " - mailbox synchronizer\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"
|
||||||
|
" -x, --expunge-solo expunge deleted messages that are not paired\n"
|
||||||
" -c, --config CONFIG read an alternate config file (default: ~/." EXE "rc)\n"
|
" -c, --config CONFIG read an alternate config file (default: ~/." EXE "rc)\n"
|
||||||
" -D, --debug debugging modes (see manual)\n"
|
" -D, --debug debugging modes (see manual)\n"
|
||||||
" -V, --verbose display what is happening\n"
|
" -V, --verbose display what is happening\n"
|
||||||
|
@ -52,7 +53,8 @@ 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, --gone, --flags, nor --upgrade are specified, all are\n"
|
"If neither --new, --gone, --flags, nor --upgrade are specified, all are\n"
|
||||||
"active. Direction and operation can be concatenated like --pull-new, etc.\n"
|
"active. Direction and operation can be concatenated like --pull-new, etc.\n"
|
||||||
"--create, --remove, and --expunge can be suffixed with -far/-near.\n"
|
"--create, --remove, --expunge, and --expunge-solo 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"
|
||||||
|
@ -235,15 +237,21 @@ main( int argc, char **argv )
|
||||||
mvars->ops[N] |= op, ms_warn = 1;
|
mvars->ops[N] |= op, ms_warn = 1;
|
||||||
else
|
else
|
||||||
goto badopt;
|
goto badopt;
|
||||||
mvars->ops[F] |= op & (XOP_HAVE_CREATE | XOP_HAVE_REMOVE | XOP_HAVE_EXPUNGE);
|
mvars->ops[F] |= op & (XOP_HAVE_CREATE | XOP_HAVE_REMOVE | XOP_HAVE_EXPUNGE | XOP_HAVE_EXPUNGE_SOLO);
|
||||||
} 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;
|
||||||
goto lcop;
|
goto lcop;
|
||||||
|
} else if (starts_with( opt, -1, "expunge-solo", 12 )) {
|
||||||
|
opt += 12;
|
||||||
|
op = OP_EXPUNGE_SOLO | XOP_HAVE_EXPUNGE_SOLO;
|
||||||
|
goto lcop;
|
||||||
} else if (starts_with( opt, -1, "expunge", 7 )) {
|
} else if (starts_with( opt, -1, "expunge", 7 )) {
|
||||||
opt += 7;
|
opt += 7;
|
||||||
op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
|
op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
|
||||||
goto lcop;
|
goto lcop;
|
||||||
|
} else if (!strcmp( opt, "no-expunge-solo" )) {
|
||||||
|
mvars->ops[F] |= XOP_EXPUNGE_SOLO_NOOP | XOP_HAVE_EXPUNGE_SOLO;
|
||||||
} else if (!strcmp( opt, "no-expunge" )) {
|
} else if (!strcmp( opt, "no-expunge" )) {
|
||||||
mvars->ops[F] |= XOP_EXPUNGE_NOOP | XOP_HAVE_EXPUNGE;
|
mvars->ops[F] |= XOP_EXPUNGE_NOOP | XOP_HAVE_EXPUNGE;
|
||||||
} else if (!strcmp( opt, "no-create" )) {
|
} else if (!strcmp( opt, "no-create" )) {
|
||||||
|
@ -340,11 +348,14 @@ main( int argc, char **argv )
|
||||||
ochar++;
|
ochar++;
|
||||||
else
|
else
|
||||||
cops |= op;
|
cops |= op;
|
||||||
mvars->ops[F] |= op & (XOP_HAVE_CREATE | XOP_HAVE_REMOVE | XOP_HAVE_EXPUNGE);
|
mvars->ops[F] |= op & (XOP_HAVE_CREATE | XOP_HAVE_REMOVE | XOP_HAVE_EXPUNGE | XOP_HAVE_EXPUNGE_SOLO);
|
||||||
break;
|
break;
|
||||||
case 'R':
|
case 'R':
|
||||||
op = OP_REMOVE|XOP_HAVE_REMOVE;
|
op = OP_REMOVE|XOP_HAVE_REMOVE;
|
||||||
goto cop;
|
goto cop;
|
||||||
|
case 'x':
|
||||||
|
op = OP_EXPUNGE_SOLO | XOP_HAVE_EXPUNGE_SOLO;
|
||||||
|
goto cop;
|
||||||
case 'X':
|
case 'X':
|
||||||
op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
|
op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
|
||||||
goto cop;
|
goto cop;
|
||||||
|
|
|
@ -186,13 +186,20 @@ add_channel( chan_ent_t ***chanapp, channel_conf_t *chan, int ops[] )
|
||||||
merge_actions( chan, ops, XOP_HAVE_CREATE, OP_CREATE, 0 );
|
merge_actions( chan, ops, XOP_HAVE_CREATE, OP_CREATE, 0 );
|
||||||
merge_actions( chan, ops, XOP_HAVE_REMOVE, OP_REMOVE, 0 );
|
merge_actions( chan, ops, XOP_HAVE_REMOVE, OP_REMOVE, 0 );
|
||||||
merge_actions( chan, ops, XOP_HAVE_EXPUNGE, OP_EXPUNGE, 0 );
|
merge_actions( chan, ops, XOP_HAVE_EXPUNGE, OP_EXPUNGE, 0 );
|
||||||
|
merge_actions( chan, ops, XOP_HAVE_EXPUNGE_SOLO, OP_EXPUNGE_SOLO, 0 );
|
||||||
debug( "channel ops (%s):\n far: %s\n near: %s\n",
|
debug( "channel ops (%s):\n far: %s\n near: %s\n",
|
||||||
chan->name, fmt_ops( ops[F] ).str, fmt_ops( ops[N] ).str );
|
chan->name, fmt_ops( ops[F] ).str, fmt_ops( ops[N] ).str );
|
||||||
|
|
||||||
for (int t = 0; t < 2; t++) {
|
for (int t = 0; t < 2; t++) {
|
||||||
|
if (!(~ops[t] & (OP_EXPUNGE | OP_EXPUNGE_SOLO))) {
|
||||||
|
error( "Specified both Expunge and ExpungeSolo for %s of Channel '%s'.\n",
|
||||||
|
str_fn[t], chan->stores[t]->name );
|
||||||
|
free( ce );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
if (chan->ops[t] & OP_MASK_TYPE)
|
if (chan->ops[t] & OP_MASK_TYPE)
|
||||||
ops_any[t] = 1;
|
ops_any[t] = 1;
|
||||||
if ((chan->ops[t] & OP_EXPUNGE) &&
|
if ((chan->ops[t] & (OP_EXPUNGE | OP_EXPUNGE_SOLO)) &&
|
||||||
(chan->stores[t]->trash ||
|
(chan->stores[t]->trash ||
|
||||||
(chan->stores[t^1]->trash && chan->stores[t^1]->trash_remote_new)))
|
(chan->stores[t^1]->trash && chan->stores[t^1]->trash_remote_new)))
|
||||||
trash_any[t] = 1;
|
trash_any[t] = 1;
|
||||||
|
@ -253,6 +260,8 @@ add_named_channel( chan_ent_t ***chanapp, char *channame, int ops[] )
|
||||||
}
|
}
|
||||||
|
|
||||||
chan_ent_t *ce = add_channel( chanapp, chan, ops );
|
chan_ent_t *ce = add_channel( chanapp, chan, ops );
|
||||||
|
if (!ce)
|
||||||
|
return NULL;
|
||||||
ce->boxes = boxes;
|
ce->boxes = boxes;
|
||||||
ce->boxlist = boxlist;
|
ce->boxlist = boxlist;
|
||||||
return ce;
|
return ce;
|
||||||
|
@ -297,7 +306,8 @@ sync_chans( core_vars_t *cvars, char **argv )
|
||||||
|
|
||||||
if (cvars->all) {
|
if (cvars->all) {
|
||||||
for (channel_conf_t *chan = channels; chan; chan = chan->next) {
|
for (channel_conf_t *chan = channels; chan; chan = chan->next) {
|
||||||
add_channel( &chanapp, chan, cvars->ops );
|
if (!add_channel( &chanapp, chan, cvars->ops ))
|
||||||
|
cvars->ret = 1;
|
||||||
if (!chan->patterns)
|
if (!chan->patterns)
|
||||||
boxes_total++;
|
boxes_total++;
|
||||||
}
|
}
|
||||||
|
|
16
src/mbsync.1
16
src/mbsync.1
|
@ -659,10 +659,24 @@ Note that for safety, non-empty mailboxes are never deleted.
|
||||||
\fBExpunge\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
|
\fBExpunge\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
|
||||||
Permanently remove all messages [on the far/near side] which are marked
|
Permanently remove all messages [on the far/near side] which are marked
|
||||||
for deletion.
|
for deletion.
|
||||||
|
Mutually exclusive with \fBExpungeSolo\fR for the same side.
|
||||||
See \fBRECOMMENDATIONS\fR below.
|
See \fBRECOMMENDATIONS\fR below.
|
||||||
(Global default: \fBNone\fR)
|
(Global default: \fBNone\fR)
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
|
\fBExpungeSolo\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
|
||||||
|
Permanently remove all messages [on the far/near side] which are both
|
||||||
|
marked for deletion and have no corresponding message in the opposite
|
||||||
|
Store.
|
||||||
|
Together with \fBSync Gone\fR, this allows actual mirroring of
|
||||||
|
expunges. Note, however, that this makes sense only if nothing else
|
||||||
|
expunges the other messages which are marked for deletion.
|
||||||
|
Also note that this does not work for IMAP Stores which do not support
|
||||||
|
the UIDPLUS extension.
|
||||||
|
Mutually exclusive with \fBExpunge\fR for the same side.
|
||||||
|
(Global default: \fBNone\fR)
|
||||||
|
.
|
||||||
|
.TP
|
||||||
\fBCopyArrivalDate\fR {\fByes\fR|\fBno\fR}
|
\fBCopyArrivalDate\fR {\fByes\fR|\fBno\fR}
|
||||||
Selects whether their arrival time should be propagated together with
|
Selects whether their arrival time should be propagated together with
|
||||||
the messages.
|
the messages.
|
||||||
|
@ -673,7 +687,7 @@ date\fR) is actually the arrival time, but it is usually close enough.
|
||||||
(Global default: \fBno\fR)
|
(Global default: \fBno\fR)
|
||||||
.
|
.
|
||||||
.P
|
.P
|
||||||
\fBSync\fR, \fBCreate\fR, \fBRemove\fR, \fBExpunge\fR,
|
\fBSync\fR, \fBCreate\fR, \fBRemove\fR, \fBExpunge\fR, \fBExpungeSolo\fR,
|
||||||
\fBMaxMessages\fR, \fBExpireUnread\fR, and \fBCopyArrivalDate\fR
|
\fBMaxMessages\fR, \fBExpireUnread\fR, and \fBCopyArrivalDate\fR
|
||||||
can be used before any section for a global effect.
|
can be used before any section for a global effect.
|
||||||
The global settings are overridden by Channel-specific options,
|
The global settings are overridden by Channel-specific options,
|
||||||
|
|
100
src/run-tests.pl
100
src/run-tests.pl
|
@ -1816,4 +1816,104 @@ my @X13 = (
|
||||||
);
|
);
|
||||||
test("trash new remotely", \@x10, \@X13, \@O13);
|
test("trash new remotely", \@x10, \@X13, \@O13);
|
||||||
|
|
||||||
|
# Test "mirroring" expunges.
|
||||||
|
|
||||||
|
my @xa0 = (
|
||||||
|
M, 0, M,
|
||||||
|
# pair
|
||||||
|
A, "*", "*", "*",
|
||||||
|
# expire
|
||||||
|
B, "*", "*", "*S",
|
||||||
|
# expire with del
|
||||||
|
C, "*T", "*", "*S",
|
||||||
|
# pair flag del
|
||||||
|
D, "*T", "*", "*",
|
||||||
|
E, "*", "*", "*T",
|
||||||
|
# pair flag undel
|
||||||
|
F, "*", "*T", "*T",
|
||||||
|
G, "*T", "*T", "*",
|
||||||
|
# pair gone
|
||||||
|
H, "_", "*", "*",
|
||||||
|
I, "*", "*", "_",
|
||||||
|
# upgrade
|
||||||
|
J, "**", "*>", "*F?",
|
||||||
|
K, "*F?", "*<", "**",
|
||||||
|
# doomed upgrade
|
||||||
|
L, "*T*", "*>", "*F?",
|
||||||
|
M, "*F?", "*<", "*T*",
|
||||||
|
# doomed new
|
||||||
|
N, "", "", "*T",
|
||||||
|
O, "*T", "", "",
|
||||||
|
);
|
||||||
|
|
||||||
|
my @Oa1 = ("", "", "ExpungeSolo Both\nMaxMessages 1\nExpireUnread false\n");
|
||||||
|
my @Xa1 = (
|
||||||
|
N, B, O,
|
||||||
|
B, "+S", "/", "/",
|
||||||
|
C, "+S", "+ST", "+T", # This is weird, but it's not worth handling.
|
||||||
|
D, "", "+T", "+T",
|
||||||
|
E, "+T", "+T", "",
|
||||||
|
F, "", "-T", "-T",
|
||||||
|
G, "-T", "-T", "",
|
||||||
|
H, "", "/", "/",
|
||||||
|
I, "/", "/", "",
|
||||||
|
J, "", ">->", "^*",
|
||||||
|
J, "", "", "&1/",
|
||||||
|
K, "^*", "<-<", "",
|
||||||
|
K, "&1/", "", "",
|
||||||
|
L, "", ">->+T", "^*T",
|
||||||
|
L, "", "", "&1/",
|
||||||
|
M, "^*T", "<-<+T", "",
|
||||||
|
M, "&1/", "", "",
|
||||||
|
N, "*T", "*T", "",
|
||||||
|
O, "", "*T", "*T",
|
||||||
|
);
|
||||||
|
test("expunge solo both", \@xa0, \@Xa1, \@Oa1);
|
||||||
|
|
||||||
|
my @Oa2 = ("", "", "ExpungeSolo Near\nMaxMessages 1\nExpireUnread false\n");
|
||||||
|
my @Xa2 = (
|
||||||
|
N, B, O,
|
||||||
|
B, "+S", "/", "/",
|
||||||
|
C, "+S", "+ST", "+T", # As above.
|
||||||
|
D, "", "+T", "+T",
|
||||||
|
E, "+T", "+T", "",
|
||||||
|
F, "", "-T", "-T",
|
||||||
|
G, "-T", "-T", "",
|
||||||
|
H, "", "/", "/",
|
||||||
|
I, "+T", ">", "",
|
||||||
|
J, "", ">->", "^*",
|
||||||
|
J, "", "", "&1/",
|
||||||
|
K, "^*", "<-<", "",
|
||||||
|
K, "&1+T", "^", "|",
|
||||||
|
L, "", ">->+T", "^*T",
|
||||||
|
L, "", "", "&1/",
|
||||||
|
M, "^*T", "<-<+T", "",
|
||||||
|
M, "&1+T", "^", "|",
|
||||||
|
N, "*T", "*T", "",
|
||||||
|
O, "", "*T", "*T",
|
||||||
|
);
|
||||||
|
test("expunge solo near", \@xa0, \@Xa2, \@Oa2);
|
||||||
|
|
||||||
|
my @Oa3 = ("", "", "Expunge Far\nExpungeSolo Near\nMaxMessages 1\nExpireUnread false\n");
|
||||||
|
my @Xa3 = (
|
||||||
|
K, B, J,
|
||||||
|
B, "+S", "/", "/",
|
||||||
|
C, "/", "/", "/",
|
||||||
|
D, "/", "/", "/",
|
||||||
|
E, "/", "/", "/",
|
||||||
|
F, "", "-T", "-T",
|
||||||
|
G, "-T", "-T", "",
|
||||||
|
H, "", "/", "/",
|
||||||
|
I, "/", "/", "",
|
||||||
|
J, "", ">->", "^*",
|
||||||
|
J, "", "", "&1/",
|
||||||
|
K, "^*", "<-<", "",
|
||||||
|
K, "&1/", "", "",
|
||||||
|
L, "/", "/", "/",
|
||||||
|
M, "/", "/", "/",
|
||||||
|
N, "", "", "/",
|
||||||
|
O, "/", "", "",
|
||||||
|
);
|
||||||
|
test("expunge far & solo near", \@xa0, \@Xa3, \@Oa3);
|
||||||
|
|
||||||
print "OK.\n";
|
print "OK.\n";
|
||||||
|
|
69
src/sync.c
69
src/sync.c
|
@ -788,9 +788,12 @@ box_opened2( sync_vars_t *svars, int t )
|
||||||
if ((chan->ops[t] | chan->ops[t^1]) & OP_EXPUNGE) // Don't propagate doomed msgs
|
if ((chan->ops[t] | chan->ops[t^1]) & OP_EXPUNGE) // Don't propagate doomed msgs
|
||||||
opts[t^1] |= OPEN_FLAGS;
|
opts[t^1] |= OPEN_FLAGS;
|
||||||
}
|
}
|
||||||
if (chan->ops[t] & OP_EXPUNGE) {
|
if (chan->ops[t] & (OP_EXPUNGE | OP_EXPUNGE_SOLO)) {
|
||||||
opts[t] |= OPEN_EXPUNGE;
|
opts[t] |= OPEN_EXPUNGE;
|
||||||
if (chan->stores[t]->trash) {
|
if (chan->ops[t] & OP_EXPUNGE_SOLO) {
|
||||||
|
opts[t] |= OPEN_OLD | OPEN_NEW | OPEN_FLAGS | OPEN_UID_EXPUNGE;
|
||||||
|
opts[t^1] |= OPEN_OLD;
|
||||||
|
} else if (chan->stores[t]->trash) {
|
||||||
if (!chan->stores[t]->trash_only_new)
|
if (!chan->stores[t]->trash_only_new)
|
||||||
opts[t] |= OPEN_OLD;
|
opts[t] |= OPEN_OLD;
|
||||||
opts[t] |= OPEN_NEW | OPEN_FLAGS | OPEN_UID_EXPUNGE;
|
opts[t] |= OPEN_NEW | OPEN_FLAGS | OPEN_UID_EXPUNGE;
|
||||||
|
@ -816,6 +819,11 @@ box_opened2( sync_vars_t *svars, int t )
|
||||||
for (t = 0; t < 2; t++) {
|
for (t = 0; t < 2; t++) {
|
||||||
svars->opts[t] = svars->drv[t]->prepare_load_box( ctx[t], opts[t] );
|
svars->opts[t] = svars->drv[t]->prepare_load_box( ctx[t], opts[t] );
|
||||||
if (opts[t] & ~svars->opts[t] & OPEN_UID_EXPUNGE) {
|
if (opts[t] & ~svars->opts[t] & OPEN_UID_EXPUNGE) {
|
||||||
|
if (chan->ops[t] & OP_EXPUNGE_SOLO) {
|
||||||
|
error( "Error: Store %s does not support ExpungeSolo.\n",
|
||||||
|
svars->chan->stores[t]->name );
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
if (!ctx[t]->racy_trash) {
|
if (!ctx[t]->racy_trash) {
|
||||||
ctx[t]->racy_trash = 1;
|
ctx[t]->racy_trash = 1;
|
||||||
notice( "Notice: Trashing in Store %s is prone to race conditions.\n",
|
notice( "Notice: Trashing in Store %s is prone to race conditions.\n",
|
||||||
|
@ -1490,7 +1498,8 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux
|
||||||
dflags |= F_DELETED;
|
dflags |= F_DELETED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((svars->chan->ops[t] & OP_EXPUNGE) && (((srec->msg[t] ? srec->msg[t]->flags : 0) | aflags) & ~dflags & F_DELETED) &&
|
if ((svars->chan->ops[t] & OP_EXPUNGE) &&
|
||||||
|
(((srec->msg[t] ? srec->msg[t]->flags : 0) | aflags) & ~dflags & F_DELETED) &&
|
||||||
(!svars->ctx[t]->conf->trash || svars->ctx[t]->conf->trash_only_new))
|
(!svars->ctx[t]->conf->trash || svars->ctx[t]->conf->trash_only_new))
|
||||||
{
|
{
|
||||||
/* If the message is going to be expunged, don't propagate anything but the deletion. */
|
/* If the message is going to be expunged, don't propagate anything but the deletion. */
|
||||||
|
@ -1748,8 +1757,54 @@ msgs_flags_set( sync_vars_t *svars, int t )
|
||||||
if (check_cancel( svars ))
|
if (check_cancel( svars ))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (!(svars->chan->ops[t] & OP_EXPUNGE))
|
int only_solo;
|
||||||
|
if (svars->chan->ops[t] & OP_EXPUNGE_SOLO)
|
||||||
|
only_solo = 1;
|
||||||
|
else if (svars->chan->ops[t] & OP_EXPUNGE)
|
||||||
|
only_solo = 0;
|
||||||
|
else
|
||||||
goto skip;
|
goto skip;
|
||||||
|
int expunge_other = (svars->chan->ops[t^1] & OP_EXPUNGE);
|
||||||
|
// Driver-wise, this makes sense only if (svars->opts[t] & OPEN_UID_EXPUNGE),
|
||||||
|
// but the trashing loop uses the result as well.
|
||||||
|
debug( "preparing expunge of %s on %s, %sexpunging %s\n",
|
||||||
|
only_solo ? "solo" : "all", str_fn[t], expunge_other ? "" : "NOT ", str_fn[t^1] );
|
||||||
|
for (tmsg = svars->msgs[t]; tmsg; tmsg = tmsg->next) {
|
||||||
|
if (tmsg->status & M_DEAD)
|
||||||
|
continue;
|
||||||
|
if (!(tmsg->flags & F_DELETED)) {
|
||||||
|
//debug( " message %u is not deleted\n", tmsg->uid ); // Too noisy
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
debugn( " message %u ", tmsg->uid );
|
||||||
|
if (only_solo) {
|
||||||
|
if ((srec = tmsg->srec)) {
|
||||||
|
if (!srec->uid[t^1]) {
|
||||||
|
debugn( "(solo) " );
|
||||||
|
} else if (srec->status & S_GONE(t^1)) {
|
||||||
|
debugn( "(orphaned) " );
|
||||||
|
} else if (expunge_other && (srec->status & S_DEL(t^1))) {
|
||||||
|
debugn( "(orphaning) " );
|
||||||
|
} else if (t == N && (srec->status & (S_EXPIRE | S_EXPIRED))) {
|
||||||
|
// Expiration overrides mirroring, as otherwise the combination
|
||||||
|
// makes no sense at all.
|
||||||
|
debugn( "(expire) " );
|
||||||
|
} else {
|
||||||
|
debug( "is not solo\n" );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (srec->status & S_PENDING) {
|
||||||
|
debug( "is being paired\n" );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debugn( "(isolated) " );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug( "- expunging\n" );
|
||||||
|
tmsg->status |= M_EXPUNGE;
|
||||||
|
}
|
||||||
|
|
||||||
int remote, only_new;
|
int remote, only_new;
|
||||||
if (svars->ctx[t]->conf->trash) {
|
if (svars->ctx[t]->conf->trash) {
|
||||||
only_new = svars->ctx[t]->conf->trash_only_new;
|
only_new = svars->ctx[t]->conf->trash_only_new;
|
||||||
|
@ -1765,8 +1820,8 @@ msgs_flags_set( sync_vars_t *svars, int t )
|
||||||
for (tmsg = svars->msgs[t]; tmsg; tmsg = tmsg->next) {
|
for (tmsg = svars->msgs[t]; tmsg; tmsg = tmsg->next) {
|
||||||
if (tmsg->status & M_DEAD)
|
if (tmsg->status & M_DEAD)
|
||||||
continue;
|
continue;
|
||||||
if (!(tmsg->flags & F_DELETED)) {
|
if (!(tmsg->status & M_EXPUNGE)) {
|
||||||
//debug( " message %u is not deleted\n", tmsg->uid ); // Too noisy
|
//debug( " message %u is not being expunged\n", tmsg->uid ); // Too noisy
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
debugn( " message %u ", tmsg->uid );
|
debugn( " message %u ", tmsg->uid );
|
||||||
|
@ -1881,7 +1936,7 @@ sync_close( sync_vars_t *svars, int t )
|
||||||
return;
|
return;
|
||||||
svars->state[t] |= ST_CLOSING;
|
svars->state[t] |= ST_CLOSING;
|
||||||
|
|
||||||
if ((svars->chan->ops[t] & OP_EXPUNGE) && !(DFlags & FAKEEXPUNGE)
|
if ((svars->chan->ops[t] & (OP_EXPUNGE | OP_EXPUNGE_SOLO)) && !(DFlags & FAKEEXPUNGE)
|
||||||
/*&& !(svars->state[t] & ST_TRASH_BAD)*/) {
|
/*&& !(svars->state[t] & ST_TRASH_BAD)*/) {
|
||||||
debug( "expunging %s\n", str_fn[t] );
|
debug( "expunging %s\n", str_fn[t] );
|
||||||
svars->drv[t]->close_box( svars->ctx[t], box_closed, AUX );
|
svars->drv[t]->close_box( svars->ctx[t], box_closed, AUX );
|
||||||
|
|
|
@ -21,6 +21,7 @@ BIT_ENUM(
|
||||||
OP_GONE,
|
OP_GONE,
|
||||||
OP_FLAGS,
|
OP_FLAGS,
|
||||||
OP_EXPUNGE,
|
OP_EXPUNGE,
|
||||||
|
OP_EXPUNGE_SOLO,
|
||||||
OP_CREATE,
|
OP_CREATE,
|
||||||
OP_REMOVE,
|
OP_REMOVE,
|
||||||
|
|
||||||
|
@ -29,12 +30,14 @@ BIT_ENUM(
|
||||||
XOP_HAVE_TYPE, // Aka mode; have at least one of dir and type (see below)
|
XOP_HAVE_TYPE, // Aka mode; have at least one of dir and type (see below)
|
||||||
// The following must all have the same bit shift from the corresponding OP_* flags.
|
// The following must all have the same bit shift from the corresponding OP_* flags.
|
||||||
XOP_HAVE_EXPUNGE,
|
XOP_HAVE_EXPUNGE,
|
||||||
|
XOP_HAVE_EXPUNGE_SOLO,
|
||||||
XOP_HAVE_CREATE,
|
XOP_HAVE_CREATE,
|
||||||
XOP_HAVE_REMOVE,
|
XOP_HAVE_REMOVE,
|
||||||
// ... until here.
|
// ... until here.
|
||||||
XOP_TYPE_NOOP,
|
XOP_TYPE_NOOP,
|
||||||
// ... and here again from scratch.
|
// ... and here again from scratch.
|
||||||
XOP_EXPUNGE_NOOP,
|
XOP_EXPUNGE_NOOP,
|
||||||
|
XOP_EXPUNGE_SOLO_NOOP,
|
||||||
XOP_CREATE_NOOP,
|
XOP_CREATE_NOOP,
|
||||||
XOP_REMOVE_NOOP,
|
XOP_REMOVE_NOOP,
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user