isync

mailbox synchronization program
git clone https://git.code.sf.net/p/isync/isync
Log | Files | Refs | README | LICENSE

commit eab3874918f9c80db69a1b72d2f546efba4caa01
parent f2f519e20b75a0cfb21198ddaa5e24b72f92f5f8
Author: Oswald Buddenhagen <ossi@users.sf.net>
Date:   Wed,  1 Jun 2022 14:04:12 +0200

orphan/prune sync entries also if messages were expunged externally

deletions we propagated ourselves are implicitly covered by that as
well, so we don't need to record them separately anymore.

Diffstat:
Msrc/run-tests.pl | 24++++++++++++++++++++++++
Msrc/sync.c | 44++++++++++++++++++++++----------------------
Msrc/sync_p.h | 1+
3 files changed, 47 insertions(+), 22 deletions(-)

diff --git a/src/run-tests.pl b/src/run-tests.pl @@ -941,6 +941,7 @@ sub test($$$$) my @x01 = ( I, 0, I, + P, "_", "*", "_", R, "*", "", "", # Skipped/failed messages to prevent maxuid topping S, "", "", "*", A, "*F", "*", "*", @@ -949,6 +950,8 @@ my @x01 = ( D, "*", "*", "*", E, "*T", "*", "*", F, "*", "*", "*T", + O, "*T", "*T", "*T", + Q, "_", "*T", "*T", G, "*F", "*", "_", H, "*FT", "*", "*", I, "_", "*", "*", @@ -961,11 +964,13 @@ my @x01 = ( my @O01 = ("", "", ""); my @X01 = ( M, 0, K, + P, "", "/", "", A, "", "+F", "+F", B, "+F", "+F", "", C, "", "+FS", "+S", E, "", "+T", "+T", F, "+T", "+T", "", + Q, "", "<", "", G, "+T", ">", "", H, "", "+FT", "+FT", I, "", "<", "+T", @@ -979,11 +984,14 @@ test("full", \@x01, \@X01, \@O01); my @O02 = ("", "", "Expunge Both\n"); my @X02 = ( M, 0, K, + P, "", "/", "", A, "", "+F", "+F", B, "+F", "+F", "", C, "", "+FS", "+S", E, "/", "/", "/", F, "/", "/", "/", + O, "/", "/", "/", + Q, "", "/", "/", G, "/", "/", "", H, "/", "/", "/", I, "", "/", "/", @@ -997,11 +1005,14 @@ test("full + expunge both", \@x01, \@X02, \@O02); my @O03 = ("", "", "Expunge Near\n"); my @X03 = ( M, 0, K, + P, "", "/", "", A, "", "+F", "+F", B, "+F", "+F", "", C, "", "+FS", "+S", E, "", ">+T", "/", F, "+T", ">+T", "/", + O, "", ">", "/", + Q, "", "/", "/", G, "+T", ">", "", H, "", ">+T", "/", I, "", "/", "/", @@ -1014,9 +1025,11 @@ test("full + expunge near side", \@x01, \@X03, \@O03); my @O04 = ("", "", "Sync Pull\n"); my @X04 = ( K, 0, I, + P, "", "/", "", # Only because test uses Maildir driver A, "", "+F", "+F", C, "", "+FS", "+S", E, "", "+T", "+T", + Q, "", "<", "", H, "", "+FT", "+FT", I, "", "<", "+T", J, "", "*T", "*T", @@ -1027,11 +1040,13 @@ test("pull", \@x01, \@X04, \@O04); my @O05 = ("", "", "Sync Flags\n"); my @X05 = ( I, 0, I, + P, "", "/", "", A, "", "+F", "+F", B, "+F", "+F", "", C, "", "+FS", "+S", E, "", "+T", "+T", F, "+T", "+T", "", + Q, "", "<", "", H, "", "+FT", "+FT", ); test("flags", \@x01, \@X05, \@O05); @@ -1039,7 +1054,9 @@ test("flags", \@x01, \@X05, \@O05); my @O06 = ("", "", "Sync Delete\n"); my @X06 = ( I, 0, I, + P, "", "/", "", G, "+T", ">", "", + Q, "", "<", "", I, "", "<", "+T", ); test("deletions", \@x01, \@X06, \@O06); @@ -1057,9 +1074,11 @@ test("new", \@x01, \@X07, \@O07); my @O08 = ("", "", "Sync PushFlags PullDelete\n"); my @X08 = ( I, 0, I, + P, "", "/", "", B, "+F", "+F", "", C, "", "+F", "", F, "+T", "+T", "", + Q, "", "<", "", I, "", "<", "+T", ); test("push flags + pull deletions", \@x01, \@X08, \@O08); @@ -1067,8 +1086,11 @@ test("push flags + pull deletions", \@x01, \@X08, \@O08); my @O09 = ("", "", "Sync None\nExpunge Both\n"); my @X09 = ( I, 0, I, + P, "", "/", "", E, "/", "", "", F, "", "", "/", + O, "/", "/", "/", + Q, "", "/", "/", H, "/", "", "", J, "/", "", "", L, "", "", "/", @@ -1080,6 +1102,8 @@ my @O0A = ("", "", "Sync None\nExpunge Near\n"); my @X0A = ( I, 0, I, F, "", "", "/", + O, "", "", "/", + Q, "", "", "/", L, "", "", "/", ); test("noop + expunge near side", \@x01, \@X0A, \@O0A); diff --git a/src/sync.c b/src/sync.c @@ -1036,6 +1036,7 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux } else if (del[t]) { // The target was newly expunged, so there is nothing to update. // The deletion is propagated in the opposite iteration. + srec->status |= S_GONE(t); } else if (!srec->uid[t]) { // The target was never stored, or was previously expunged, so there // is nothing to update. @@ -1340,7 +1341,7 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux for (t = 0; t < 2; t++) { if (!srec->uid[t]) continue; - if (!srec->msg[t] && (svars->opts[t] & OPEN_OLD)) { + if (srec->status & S_GONE(t)) { // The message was expunged. No need to call flags_set(), because: // - for S_DELETE and S_PURGE, the entry will be pruned due to both sides being gone // - for regular flag propagations, there is nothing to do @@ -1571,10 +1572,7 @@ flags_set_p2( sync_vars_t *svars, sync_rec_t *srec, int t ) if (srec->status & S_PURGE) { JLOG( "P %u %u", (srec->uid[F], srec->uid[N]), "deleted dummy" ); srec->status = (srec->status & ~S_PURGE) | S_PURGED; - } else if (srec->status & S_DELETE) { - JLOG( "%c %u %u 0", ("><"[t], srec->uid[F], srec->uid[N]), "%sed deletion", str_hl[t] ); - srec->uid[t^1] = 0; - } else { + } else if (!(srec->status & S_DELETE)) { uchar nflags = (srec->flags | srec->aflags[t]) & ~srec->dflags[t]; if (srec->flags != nflags) { JLOG( "* %u %u %u", (srec->uid[F], srec->uid[N], nflags), "%sed flags %s; were %s", @@ -1802,24 +1800,26 @@ box_closed_p2( sync_vars_t *svars, int t ) PC_JLOG( "N %d %u", (t, svars->maxuid[t]), "up maxuid of %s", str_fn[t] ); } - if (((svars->state[F] | svars->state[N]) & ST_DID_EXPUNGE) || svars->chan->max_messages) { - debug( "purging obsolete entries\n" ); - for (srec = svars->srecs; srec; srec = srec->next) { - if (srec->status & S_DEAD) - continue; - if (!srec->uid[N] || ((srec->status & S_DEL(N)) && (svars->state[N] & ST_DID_EXPUNGE))) { - if (!srec->uid[F] || ((srec->status & S_DEL(F)) && (svars->state[F] & ST_DID_EXPUNGE)) || - ((srec->status & S_EXPIRED) && svars->maxuid[F] >= srec->uid[F] && svars->maxxfuid >= srec->uid[F])) { - PC_JLOG( "- %u %u", (srec->uid[F], srec->uid[N]), "killing" ); - srec->status = S_DEAD; - } else if (srec->uid[N] && (srec->status & S_DEL(F))) { - PC_JLOG( "> %u %u 0", (srec->uid[F], srec->uid[N]), "orphaning" ); - srec->uid[N] = 0; - } - } else if (srec->uid[F] && ((srec->status & S_DEL(F)) && (svars->state[F] & ST_DID_EXPUNGE)) && (srec->status & S_DEL(N))) { - PC_JLOG( "< %u %u 0", (srec->uid[F], srec->uid[N]), "orphaning" ); - srec->uid[F] = 0; + debug( "purging obsolete entries\n" ); + for (srec = svars->srecs; srec; srec = srec->next) { + if (srec->status & S_DEAD) + continue; + if ((srec->status & S_DEL(F)) && (svars->state[F] & ST_DID_EXPUNGE)) + srec->status |= S_GONE(F); + if ((srec->status & S_DEL(N)) && (svars->state[N] & ST_DID_EXPUNGE)) + srec->status |= S_GONE(N); + if (!srec->uid[N] || (srec->status & S_GONE(N))) { + if (!srec->uid[F] || (srec->status & S_GONE(F)) || + ((srec->status & S_EXPIRED) && svars->maxuid[F] >= srec->uid[F] && svars->maxxfuid >= srec->uid[F])) { + PC_JLOG( "- %u %u", (srec->uid[F], srec->uid[N]), "killing" ); + srec->status = S_DEAD; + } else if (srec->uid[N] && (srec->status & S_DEL(F))) { + PC_JLOG( "> %u %u 0", (srec->uid[F], srec->uid[N]), "orphaning" ); + srec->uid[N] = 0; } + } else if (srec->uid[F] && (srec->status & S_GONE(F)) && (srec->status & S_DEL(N))) { + PC_JLOG( "< %u %u 0", (srec->uid[F], srec->uid[N]), "orphaning" ); + srec->uid[F] = 0; } } diff --git a/src/sync_p.h b/src/sync_p.h @@ -17,6 +17,7 @@ BIT_ENUM( S_PENDING, // the entry is new and awaits propagation (possibly a retry) S_DUMMY(2), // f/n message is only a placeholder S_SKIPPED, // pre-1.4 legacy: the entry was not propagated (message is too big) + S_GONE(2), // ephemeral: f/n message has been expunged S_DEL(2), // ephemeral: f/n message would be subject to expunge S_DELETE, // ephemeral: flags propagation is a deletion S_UPGRADE, // ephemeral: upgrading placeholder, do not apply MaxSize