|
|
@ -136,20 +136,23 @@ make_flags( uchar flags, char *buf ) |
|
|
|
#define S_EXPIRE (1<<0) // the entry is being expired (near side message removal scheduled)
|
|
|
|
#define S_EXPIRE (1<<0) // the entry is being expired (near side message removal scheduled)
|
|
|
|
#define S_EXPIRED (1<<1) // the entry is expired (near side message removal confirmed)
|
|
|
|
#define S_EXPIRED (1<<1) // the entry is expired (near side message removal confirmed)
|
|
|
|
#define S_PENDING (1<<2) // the entry is new and awaits propagation (possibly a retry)
|
|
|
|
#define S_PENDING (1<<2) // the entry is new and awaits propagation (possibly a retry)
|
|
|
|
#define S_SKIPPED (1<<3) // the entry was not propagated (message is too big)
|
|
|
|
#define S_DUMMY(fn) (1<<(3+(fn))) // f/n message is only a placeholder
|
|
|
|
|
|
|
|
#define S_SKIPPED (1<<5) // pre-1.4 legacy: the entry was not propagated (message is too big)
|
|
|
|
#define S_DEAD (1<<7) // ephemeral: the entry was killed and should be ignored
|
|
|
|
#define S_DEAD (1<<7) // ephemeral: the entry was killed and should be ignored
|
|
|
|
|
|
|
|
|
|
|
|
// Ephemeral working set.
|
|
|
|
// Ephemeral working set.
|
|
|
|
#define W_NEXPIRE (1<<0) // temporary: new expiration state
|
|
|
|
#define W_NEXPIRE (1<<0) // temporary: new expiration state
|
|
|
|
#define W_DELETE (1<<1) // ephemeral: flags propagation is a deletion
|
|
|
|
#define W_DELETE (1<<1) // ephemeral: flags propagation is a deletion
|
|
|
|
#define W_DEL(fn) (1<<(2+(fn))) // ephemeral: f/n message would be subject to expunge
|
|
|
|
#define W_DEL(fn) (1<<(2+(fn))) // ephemeral: f/n message would be subject to expunge
|
|
|
|
|
|
|
|
#define W_UPGRADE (1<<4) // ephemeral: upgrading placeholder, do not apply MaxSize
|
|
|
|
|
|
|
|
#define W_PURGE (1<<5) // ephemeral: placeholder is being nuked
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct sync_rec { |
|
|
|
typedef struct sync_rec { |
|
|
|
struct sync_rec *next; |
|
|
|
struct sync_rec *next; |
|
|
|
/* string_list_t *keywords; */ |
|
|
|
/* string_list_t *keywords; */ |
|
|
|
uint uid[2]; |
|
|
|
uint uid[2]; |
|
|
|
message_t *msg[2]; |
|
|
|
message_t *msg[2]; |
|
|
|
uchar status, wstate, flags, aflags[2], dflags[2]; |
|
|
|
uchar status, wstate, flags, pflags, aflags[2], dflags[2]; |
|
|
|
char tuid[TUIDL]; |
|
|
|
char tuid[TUIDL]; |
|
|
|
} sync_rec_t; |
|
|
|
} sync_rec_t; |
|
|
|
|
|
|
|
|
|
|
@ -274,6 +277,7 @@ assign_uid( sync_vars_t *svars, sync_rec_t *srec, int t, uint uid ) |
|
|
|
if (uid == svars->maxuid[t] + 1) |
|
|
|
if (uid == svars->maxuid[t] + 1) |
|
|
|
svars->maxuid[t] = uid; |
|
|
|
svars->maxuid[t] = uid; |
|
|
|
srec->status &= ~S_PENDING; |
|
|
|
srec->status &= ~S_PENDING; |
|
|
|
|
|
|
|
srec->wstate &= ~W_UPGRADE; |
|
|
|
srec->tuid[0] = 0; |
|
|
|
srec->tuid[0] = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -353,6 +357,7 @@ typedef struct copy_vars { |
|
|
|
sync_rec_t *srec; /* also ->tuid */ |
|
|
|
sync_rec_t *srec; /* also ->tuid */ |
|
|
|
message_t *msg; |
|
|
|
message_t *msg; |
|
|
|
msg_data_t data; |
|
|
|
msg_data_t data; |
|
|
|
|
|
|
|
int minimal; |
|
|
|
} copy_vars_t; |
|
|
|
} copy_vars_t; |
|
|
|
|
|
|
|
|
|
|
|
static void msg_fetched( int sts, void *aux ); |
|
|
|
static void msg_fetched( int sts, void *aux ); |
|
|
@ -365,7 +370,7 @@ copy_msg( copy_vars_t *vars ) |
|
|
|
t ^= 1; |
|
|
|
t ^= 1; |
|
|
|
vars->data.flags = vars->msg->flags; |
|
|
|
vars->data.flags = vars->msg->flags; |
|
|
|
vars->data.date = svars->chan->use_internal_date ? -1 : 0; |
|
|
|
vars->data.date = svars->chan->use_internal_date ? -1 : 0; |
|
|
|
svars->drv[t]->fetch_msg( svars->ctx[t], vars->msg, &vars->data, msg_fetched, vars ); |
|
|
|
svars->drv[t]->fetch_msg( svars->ctx[t], vars->msg, &vars->data, vars->minimal, msg_fetched, vars ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void msg_stored( int sts, uint uid, void *aux ); |
|
|
|
static void msg_stored( int sts, uint uid, void *aux ); |
|
|
@ -405,8 +410,10 @@ copy_msg_convert( int in_cr, int out_cr, copy_vars_t *vars ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
char *in_buf = vars->data.data; |
|
|
|
char *in_buf = vars->data.data; |
|
|
|
uint in_len = vars->data.len; |
|
|
|
uint in_len = vars->data.len; |
|
|
|
uint idx = 0, sbreak = 0, ebreak = 0; |
|
|
|
uint idx = 0, sbreak = 0, ebreak = 0, break2 = 0; |
|
|
|
uint lines = 0, hdr_crs = 0, bdy_crs = 0, app_cr = 0, extra = 0; |
|
|
|
uint lines = 0, hdr_crs = 0, bdy_crs = 0, app_cr = 0, extra = 0; |
|
|
|
|
|
|
|
uint add_subj = 0; |
|
|
|
|
|
|
|
|
|
|
|
if (vars->srec) { |
|
|
|
if (vars->srec) { |
|
|
|
nloop: ; |
|
|
|
nloop: ; |
|
|
|
uint start = idx; |
|
|
|
uint start = idx; |
|
|
@ -416,14 +423,29 @@ copy_msg_convert( int in_cr, int out_cr, copy_vars_t *vars ) |
|
|
|
if (c == '\r') { |
|
|
|
if (c == '\r') { |
|
|
|
line_crs++; |
|
|
|
line_crs++; |
|
|
|
} else if (c == '\n') { |
|
|
|
} else if (c == '\n') { |
|
|
|
if (starts_with_upper( in_buf + start, (int)(in_len - start), "X-TUID: ", 8 )) { |
|
|
|
if (!ebreak && starts_with_upper( in_buf + start, (int)(in_len - start), "X-TUID: ", 8 )) { |
|
|
|
extra = (sbreak = start) - (ebreak = idx); |
|
|
|
extra = (sbreak = start) - (ebreak = idx); |
|
|
|
goto oke; |
|
|
|
if (!vars->minimal) |
|
|
|
|
|
|
|
goto oke; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if (!break2 && vars->minimal && !strncasecmp( in_buf + start, "Subject:", 8 )) { |
|
|
|
|
|
|
|
break2 = start + 8; |
|
|
|
|
|
|
|
if (in_buf[break2] == ' ') |
|
|
|
|
|
|
|
break2++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
lines++; |
|
|
|
|
|
|
|
hdr_crs += line_crs; |
|
|
|
} |
|
|
|
} |
|
|
|
lines++; |
|
|
|
|
|
|
|
hdr_crs += line_crs; |
|
|
|
|
|
|
|
if (idx - line_crs - 1 == start) { |
|
|
|
if (idx - line_crs - 1 == start) { |
|
|
|
sbreak = ebreak = start; |
|
|
|
if (!ebreak) |
|
|
|
|
|
|
|
sbreak = ebreak = start; |
|
|
|
|
|
|
|
if (vars->minimal) { |
|
|
|
|
|
|
|
in_len = idx; |
|
|
|
|
|
|
|
if (!break2) { |
|
|
|
|
|
|
|
break2 = start; |
|
|
|
|
|
|
|
add_subj = 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
goto oke; |
|
|
|
goto oke; |
|
|
|
} |
|
|
|
} |
|
|
|
goto nloop; |
|
|
|
goto nloop; |
|
|
@ -449,10 +471,36 @@ copy_msg_convert( int in_cr, int out_cr, copy_vars_t *vars ) |
|
|
|
extra += lines; |
|
|
|
extra += lines; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint dummy_msg_len = 0; |
|
|
|
|
|
|
|
char dummy_msg_buf[180]; |
|
|
|
|
|
|
|
static const char dummy_pfx[] = "[placeholder] "; |
|
|
|
|
|
|
|
static const char dummy_subj[] = "Subject: [placeholder] (No Subject)"; |
|
|
|
|
|
|
|
static const char dummy_msg[] = |
|
|
|
|
|
|
|
"Having a size of %s, this message is over the MaxSize limit.%s" |
|
|
|
|
|
|
|
"Flag it and sync again (Sync mode ReNew) to fetch its real contents.%s"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (vars->minimal) { |
|
|
|
|
|
|
|
char sz[32]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (vars->msg->size < 1024000) |
|
|
|
|
|
|
|
sprintf( sz, "%dKiB", (int)(vars->msg->size >> 10) ); |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
sprintf( sz, "%.1fMiB", vars->msg->size / 1048576. ); |
|
|
|
|
|
|
|
const char *nl = app_cr ? "\r\n" : "\n"; |
|
|
|
|
|
|
|
dummy_msg_len = (uint)sprintf( dummy_msg_buf, dummy_msg, sz, nl, nl ); |
|
|
|
|
|
|
|
extra += dummy_msg_len; |
|
|
|
|
|
|
|
extra += add_subj ? strlen(dummy_subj) + app_cr + 1 : strlen(dummy_pfx); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
vars->data.len = in_len + extra; |
|
|
|
vars->data.len = in_len + extra; |
|
|
|
char *out_buf = vars->data.data = nfmalloc( vars->data.len ); |
|
|
|
char *out_buf = vars->data.data = nfmalloc( vars->data.len ); |
|
|
|
idx = 0; |
|
|
|
idx = 0; |
|
|
|
if (vars->srec) { |
|
|
|
if (vars->srec) { |
|
|
|
|
|
|
|
if (break2 && break2 < sbreak) { |
|
|
|
|
|
|
|
copy_msg_bytes( &out_buf, in_buf, &idx, break2, in_cr, out_cr ); |
|
|
|
|
|
|
|
memcpy( out_buf, dummy_pfx, strlen(dummy_pfx) ); |
|
|
|
|
|
|
|
out_buf += strlen(dummy_pfx); |
|
|
|
|
|
|
|
} |
|
|
|
copy_msg_bytes( &out_buf, in_buf, &idx, sbreak, in_cr, out_cr ); |
|
|
|
copy_msg_bytes( &out_buf, in_buf, &idx, sbreak, in_cr, out_cr ); |
|
|
|
|
|
|
|
|
|
|
|
memcpy( out_buf, "X-TUID: ", 8 ); |
|
|
|
memcpy( out_buf, "X-TUID: ", 8 ); |
|
|
@ -463,9 +511,26 @@ copy_msg_convert( int in_cr, int out_cr, copy_vars_t *vars ) |
|
|
|
*out_buf++ = '\r'; |
|
|
|
*out_buf++ = '\r'; |
|
|
|
*out_buf++ = '\n'; |
|
|
|
*out_buf++ = '\n'; |
|
|
|
idx = ebreak; |
|
|
|
idx = ebreak; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (break2 >= sbreak) { |
|
|
|
|
|
|
|
copy_msg_bytes( &out_buf, in_buf, &idx, break2, in_cr, out_cr ); |
|
|
|
|
|
|
|
if (!add_subj) { |
|
|
|
|
|
|
|
memcpy( out_buf, dummy_pfx, strlen(dummy_pfx) ); |
|
|
|
|
|
|
|
out_buf += strlen(dummy_pfx); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
memcpy( out_buf, dummy_subj, strlen(dummy_subj) ); |
|
|
|
|
|
|
|
out_buf += strlen(dummy_subj); |
|
|
|
|
|
|
|
if (app_cr) |
|
|
|
|
|
|
|
*out_buf++ = '\r'; |
|
|
|
|
|
|
|
*out_buf++ = '\n'; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
copy_msg_bytes( &out_buf, in_buf, &idx, in_len, in_cr, out_cr ); |
|
|
|
copy_msg_bytes( &out_buf, in_buf, &idx, in_len, in_cr, out_cr ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (vars->minimal) |
|
|
|
|
|
|
|
memcpy( out_buf, dummy_msg_buf, dummy_msg_len ); |
|
|
|
|
|
|
|
|
|
|
|
free( in_buf ); |
|
|
|
free( in_buf ); |
|
|
|
return 1; |
|
|
|
return 1; |
|
|
|
} |
|
|
|
} |
|
|
@ -648,6 +713,33 @@ clean_strdup( const char *s ) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static sync_rec_t * |
|
|
|
|
|
|
|
upgrade_srec( sync_vars_t *svars, sync_rec_t *srec ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Create an entry and append it to the current one.
|
|
|
|
|
|
|
|
sync_rec_t *nsrec = nfcalloc( sizeof(*nsrec) ); |
|
|
|
|
|
|
|
nsrec->next = srec->next; |
|
|
|
|
|
|
|
srec->next = nsrec; |
|
|
|
|
|
|
|
if (svars->srecadd == &srec->next) |
|
|
|
|
|
|
|
svars->srecadd = &nsrec->next; |
|
|
|
|
|
|
|
// Move the placeholder to the new entry.
|
|
|
|
|
|
|
|
int t = (srec->status & S_DUMMY(F)) ? F : N; |
|
|
|
|
|
|
|
nsrec->uid[t] = srec->uid[t]; |
|
|
|
|
|
|
|
srec->uid[t] = 0; |
|
|
|
|
|
|
|
if (srec->msg[t]) { // NULL during journal replay; is assigned later.
|
|
|
|
|
|
|
|
nsrec->msg[t] = srec->msg[t]; |
|
|
|
|
|
|
|
nsrec->msg[t]->srec = nsrec; |
|
|
|
|
|
|
|
srec->msg[t] = NULL; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Mark the original entry for upgrade.
|
|
|
|
|
|
|
|
srec->status = (srec->status & ~(S_DUMMY(F)|S_DUMMY(N))) | S_PENDING; |
|
|
|
|
|
|
|
srec->wstate |= W_UPGRADE; |
|
|
|
|
|
|
|
// Mark the placeholder for nuking.
|
|
|
|
|
|
|
|
nsrec->wstate = W_PURGE; |
|
|
|
|
|
|
|
nsrec->aflags[t] = F_DELETED; |
|
|
|
|
|
|
|
return nsrec; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int |
|
|
|
static int |
|
|
|
prepare_state( sync_vars_t *svars ) |
|
|
|
prepare_state( sync_vars_t *svars ) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -741,7 +833,8 @@ save_state( sync_vars_t *svars ) |
|
|
|
if (srec->status & S_DEAD) |
|
|
|
if (srec->status & S_DEAD) |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
make_flags( srec->flags, fbuf ); |
|
|
|
make_flags( srec->flags, fbuf ); |
|
|
|
Fprintf( svars->nfp, "%u %u %s%s\n", srec->uid[F], srec->uid[N], |
|
|
|
Fprintf( svars->nfp, "%u %u %s%s%s\n", srec->uid[F], srec->uid[N], |
|
|
|
|
|
|
|
(srec->status & S_DUMMY(F)) ? "<" : (srec->status & S_DUMMY(N)) ? ">" : "", |
|
|
|
(srec->status & S_SKIPPED) ? "^" : (srec->status & S_EXPIRED) ? "~" : "", fbuf ); |
|
|
|
(srec->status & S_SKIPPED) ? "^" : (srec->status & S_EXPIRED) ? "~" : "", fbuf ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -836,7 +929,14 @@ load_state( sync_vars_t *svars ) |
|
|
|
srec->uid[F] = t1; |
|
|
|
srec->uid[F] = t1; |
|
|
|
srec->uid[N] = t2; |
|
|
|
srec->uid[N] = t2; |
|
|
|
s = fbuf; |
|
|
|
s = fbuf; |
|
|
|
if (*s == '^') { |
|
|
|
if (*s == '<') { |
|
|
|
|
|
|
|
s++; |
|
|
|
|
|
|
|
srec->status = S_DUMMY(F); |
|
|
|
|
|
|
|
} else if (*s == '>') { |
|
|
|
|
|
|
|
s++; |
|
|
|
|
|
|
|
srec->status = S_DUMMY(N); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (*s == '^') { // Pre-1.4 legacy
|
|
|
|
s++; |
|
|
|
s++; |
|
|
|
srec->status = S_SKIPPED; |
|
|
|
srec->status = S_SKIPPED; |
|
|
|
} else if (*s == '~' || *s == 'X' /* Pre-1.3 legacy */) { |
|
|
|
} else if (*s == '~' || *s == 'X' /* Pre-1.3 legacy */) { |
|
|
@ -850,8 +950,9 @@ load_state( sync_vars_t *svars ) |
|
|
|
srec->status = S_SKIPPED; |
|
|
|
srec->status = S_SKIPPED; |
|
|
|
} |
|
|
|
} |
|
|
|
srec->flags = parse_flags( s ); |
|
|
|
srec->flags = parse_flags( s ); |
|
|
|
debug( " entry (%u,%u,%u,%s)\n", srec->uid[F], srec->uid[N], srec->flags, |
|
|
|
debug( " entry (%u,%u,%u,%s%s)\n", srec->uid[F], srec->uid[N], srec->flags, |
|
|
|
(srec->status & S_SKIPPED) ? "SKIP" : (srec->status & S_EXPIRED) ? "XPIRE" : "" ); |
|
|
|
(srec->status & S_SKIPPED) ? "SKIP" : (srec->status & S_EXPIRED) ? "XPIRE" : "", |
|
|
|
|
|
|
|
(srec->status & S_DUMMY(F)) ? ",F-DUMMY" : (srec->status & S_DUMMY(N)) ? ",N-DUMMY" : "" ); |
|
|
|
*svars->srecadd = srec; |
|
|
|
*svars->srecadd = srec; |
|
|
|
svars->srecadd = &srec->next; |
|
|
|
svars->srecadd = &srec->next; |
|
|
|
svars->nsrecs++; |
|
|
|
svars->nsrecs++; |
|
|
@ -919,14 +1020,16 @@ load_state( sync_vars_t *svars ) |
|
|
|
} |
|
|
|
} |
|
|
|
buf[ll] = 0; |
|
|
|
buf[ll] = 0; |
|
|
|
int tn; |
|
|
|
int tn; |
|
|
|
uint t1, t2, t3; |
|
|
|
uint t1, t2, t3, t4; |
|
|
|
if ((c = buf[0]) == '#' ? |
|
|
|
if ((c = buf[0]) == '#' ? |
|
|
|
(tn = 0, (sscanf( buf + 2, "%u %u %n", &t1, &t2, &tn ) < 2) || !tn || (ll - (uint)tn != TUIDL + 2)) : |
|
|
|
(tn = 0, (sscanf( buf + 2, "%u %u %n", &t1, &t2, &tn ) < 2) || !tn || (ll - (uint)tn != TUIDL + 2)) : |
|
|
|
c == '!' ? |
|
|
|
c == '!' ? |
|
|
|
(sscanf( buf + 2, "%u", &t1 ) != 1) : |
|
|
|
(sscanf( buf + 2, "%u", &t1 ) != 1) : |
|
|
|
c == 'N' || c == 'F' || c == 'T' || c == '+' || c == '&' || c == '-' || c == '=' || c == '|' ? |
|
|
|
c == 'N' || c == 'F' || c == 'T' || c == '+' || c == '&' || c == '-' || c == '=' || c == '_' || c == '|' ? |
|
|
|
(sscanf( buf + 2, "%u %u", &t1, &t2 ) != 2) : |
|
|
|
(sscanf( buf + 2, "%u %u", &t1, &t2 ) != 2) : |
|
|
|
(sscanf( buf + 2, "%u %u %u", &t1, &t2, &t3 ) != 3)) |
|
|
|
c != '^' ? |
|
|
|
|
|
|
|
(sscanf( buf + 2, "%u %u %u", &t1, &t2, &t3 ) != 3) : |
|
|
|
|
|
|
|
(sscanf( buf + 2, "%u %u %u %u", &t1, &t2, &t3, &t4 ) != 4)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
error( "Error: malformed journal entry at %s:%d\n", svars->jname, line ); |
|
|
|
error( "Error: malformed journal entry at %s:%d\n", svars->jname, line ); |
|
|
|
goto jbail; |
|
|
|
goto jbail; |
|
|
@ -992,11 +1095,24 @@ load_state( sync_vars_t *svars ) |
|
|
|
case '*': |
|
|
|
case '*': |
|
|
|
debug( "flags now %u\n", t3 ); |
|
|
|
debug( "flags now %u\n", t3 ); |
|
|
|
srec->flags = (uchar)t3; |
|
|
|
srec->flags = (uchar)t3; |
|
|
|
|
|
|
|
srec->aflags[F] = srec->aflags[N] = 0; |
|
|
|
|
|
|
|
srec->wstate &= ~W_PURGE; |
|
|
|
break; |
|
|
|
break; |
|
|
|
case '~': |
|
|
|
case '~': |
|
|
|
debug( "status now %#x\n", t3 ); |
|
|
|
debug( "status now %#x\n", t3 ); |
|
|
|
srec->status = (uchar)t3; |
|
|
|
srec->status = (uchar)t3; |
|
|
|
break; |
|
|
|
break; |
|
|
|
|
|
|
|
case '_': |
|
|
|
|
|
|
|
debug( "has placeholder now\n" ); |
|
|
|
|
|
|
|
srec->status = S_PENDING; // Pre-1.4 legacy only
|
|
|
|
|
|
|
|
srec->status |= !srec->uid[F] ? S_DUMMY(F) : S_DUMMY(N); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
case '^': |
|
|
|
|
|
|
|
debug( "is being upgraded, flags %u, srec flags %u\n", t3, t4 ); |
|
|
|
|
|
|
|
srec->pflags = (uchar)t3; |
|
|
|
|
|
|
|
srec->flags = (uchar)t4; |
|
|
|
|
|
|
|
srec = upgrade_srec( svars, srec ); |
|
|
|
|
|
|
|
break; |
|
|
|
default: |
|
|
|
default: |
|
|
|
error( "Error: unrecognized journal entry at %s:%d\n", svars->jname, line ); |
|
|
|
error( "Error: unrecognized journal entry at %s:%d\n", svars->jname, line ); |
|
|
|
goto jbail; |
|
|
|
goto jbail; |
|
|
@ -1275,18 +1391,17 @@ box_opened2( sync_vars_t *svars, int t ) |
|
|
|
} |
|
|
|
} |
|
|
|
if (chan->ops[t] & (OP_NEW|OP_RENEW)) { |
|
|
|
if (chan->ops[t] & (OP_NEW|OP_RENEW)) { |
|
|
|
opts[t] |= OPEN_APPEND; |
|
|
|
opts[t] |= OPEN_APPEND; |
|
|
|
if (chan->ops[t] & OP_RENEW) |
|
|
|
if (chan->ops[t] & OP_NEW) { |
|
|
|
opts[1-t] |= OPEN_OLD; |
|
|
|
|
|
|
|
if (chan->ops[t] & OP_NEW) |
|
|
|
|
|
|
|
opts[1-t] |= OPEN_NEW; |
|
|
|
opts[1-t] |= OPEN_NEW; |
|
|
|
if (chan->ops[t] & OP_EXPUNGE) // Don't propagate doomed msgs
|
|
|
|
if (chan->stores[t]->max_size != UINT_MAX) |
|
|
|
opts[1-t] |= OPEN_FLAGS; |
|
|
|
|
|
|
|
if (chan->stores[t]->max_size != UINT_MAX) { |
|
|
|
|
|
|
|
if (chan->ops[t] & OP_RENEW) |
|
|
|
|
|
|
|
opts[1-t] |= OPEN_FLAGS|OPEN_OLD_SIZE; |
|
|
|
|
|
|
|
if (chan->ops[t] & OP_NEW) |
|
|
|
|
|
|
|
opts[1-t] |= OPEN_FLAGS|OPEN_NEW_SIZE; |
|
|
|
opts[1-t] |= OPEN_FLAGS|OPEN_NEW_SIZE; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (chan->ops[t] & OP_RENEW) { |
|
|
|
|
|
|
|
opts[t] |= OPEN_OLD|OPEN_FLAGS|OPEN_SETFLAGS; |
|
|
|
|
|
|
|
opts[1-t] |= OPEN_OLD|OPEN_FLAGS; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (chan->ops[t] & OP_EXPUNGE) // Don't propagate doomed msgs
|
|
|
|
|
|
|
|
opts[1-t] |= OPEN_FLAGS; |
|
|
|
} |
|
|
|
} |
|
|
|
if (chan->ops[t] & OP_EXPUNGE) { |
|
|
|
if (chan->ops[t] & OP_EXPUNGE) { |
|
|
|
opts[t] |= OPEN_EXPUNGE; |
|
|
|
opts[t] |= OPEN_EXPUNGE; |
|
|
@ -1312,6 +1427,15 @@ box_opened2( sync_vars_t *svars, int t ) |
|
|
|
else |
|
|
|
else |
|
|
|
warn( "Warning: sync record (%u,%u) has stray TUID. Ignoring.\n", srec->uid[F], srec->uid[N] ); |
|
|
|
warn( "Warning: sync record (%u,%u) has stray TUID. Ignoring.\n", srec->uid[F], srec->uid[N] ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (srec->wstate & W_PURGE) { |
|
|
|
|
|
|
|
t = srec->uid[F] ? F : N; |
|
|
|
|
|
|
|
opts[t] |= OPEN_SETFLAGS; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (srec->wstate & W_UPGRADE) { |
|
|
|
|
|
|
|
t = !srec->uid[F] ? F : N; |
|
|
|
|
|
|
|
opts[t] |= OPEN_APPEND; |
|
|
|
|
|
|
|
opts[1-t] |= OPEN_OLD; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
svars->opts[F] = svars->drv[F]->prepare_load_box( ctx[F], opts[F] ); |
|
|
|
svars->opts[F] = svars->drv[F]->prepare_load_box( ctx[F], opts[F] ); |
|
|
|
svars->opts[N] = svars->drv[N]->prepare_load_box( ctx[N], opts[N] ); |
|
|
|
svars->opts[N] = svars->drv[N]->prepare_load_box( ctx[N], opts[N] ); |
|
|
@ -1583,6 +1707,12 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux |
|
|
|
debug( " near side expiring\n" ); |
|
|
|
debug( " near side expiring\n" ); |
|
|
|
sflags &= ~F_DELETED; |
|
|
|
sflags &= ~F_DELETED; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (srec->status & S_DUMMY(1-t)) { |
|
|
|
|
|
|
|
// For placeholders, don't propagate:
|
|
|
|
|
|
|
|
// - Seen, because the real contents were obviously not seen yet
|
|
|
|
|
|
|
|
// - Flagged, because it's just a request to upgrade
|
|
|
|
|
|
|
|
sflags &= ~(F_SEEN|F_FLAGGED); |
|
|
|
|
|
|
|
} |
|
|
|
srec->aflags[t] = sflags & ~srec->flags; |
|
|
|
srec->aflags[t] = sflags & ~srec->flags; |
|
|
|
srec->dflags[t] = ~sflags & srec->flags; |
|
|
|
srec->dflags[t] = ~sflags & srec->flags; |
|
|
|
if ((DFlags & DEBUG_SYNC) && (srec->aflags[t] || srec->dflags[t])) { |
|
|
|
if ((DFlags & DEBUG_SYNC) && (srec->aflags[t] || srec->dflags[t])) { |
|
|
@ -1594,6 +1724,41 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sync_rec_t *nsrec = srec; |
|
|
|
|
|
|
|
if (((srec->status & S_DUMMY(F)) && (svars->chan->ops[F] & OP_RENEW)) || |
|
|
|
|
|
|
|
((srec->status & S_DUMMY(N)) && (svars->chan->ops[N] & OP_RENEW))) { |
|
|
|
|
|
|
|
// Flagging the message on either side causes an upgrade of the dummy.
|
|
|
|
|
|
|
|
// We ignore flag resets, because that corner case is not worth it.
|
|
|
|
|
|
|
|
ushort muflags = srec->msg[F] ? srec->msg[F]->flags : 0; |
|
|
|
|
|
|
|
ushort suflags = srec->msg[N] ? srec->msg[N]->flags : 0; |
|
|
|
|
|
|
|
if ((muflags | suflags) & F_FLAGGED) { |
|
|
|
|
|
|
|
t = (srec->status & S_DUMMY(F)) ? F : N; |
|
|
|
|
|
|
|
// We calculate the flags for the replicated message already now,
|
|
|
|
|
|
|
|
// because after an interruption the dummy may be already gone.
|
|
|
|
|
|
|
|
srec->pflags = ((srec->msg[t]->flags & ~(F_SEEN|F_FLAGGED)) | srec->aflags[t]) & ~srec->dflags[t]; |
|
|
|
|
|
|
|
// Consequently, the srec's flags are committed right away as well.
|
|
|
|
|
|
|
|
srec->flags = (srec->flags | srec->aflags[t]) & ~srec->dflags[t]; |
|
|
|
|
|
|
|
JLOG( "^ %u %u %u %u", (srec->uid[F], srec->uid[N], srec->pflags, srec->flags), "upgrading placeholder" ); |
|
|
|
|
|
|
|
nsrec = upgrade_srec( svars, srec ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// This is separated, because the upgrade can come from the journal.
|
|
|
|
|
|
|
|
if (srec->wstate & W_UPGRADE) { |
|
|
|
|
|
|
|
t = !srec->uid[F] ? F : N; |
|
|
|
|
|
|
|
tmsg = srec->msg[1-t]; |
|
|
|
|
|
|
|
if ((svars->chan->ops[t] & OP_EXPUNGE) && (srec->pflags & F_DELETED)) { |
|
|
|
|
|
|
|
JLOG( "- %u %u", (srec->uid[F], srec->uid[N]), "killing upgrade - would be expunged anyway" ); |
|
|
|
|
|
|
|
tmsg->srec = NULL; |
|
|
|
|
|
|
|
srec->status = S_DEAD; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// Pretend that the source message has the adjusted flags of the dummy.
|
|
|
|
|
|
|
|
tmsg->flags = srec->pflags; |
|
|
|
|
|
|
|
tmsg->status = M_FLAGS; |
|
|
|
|
|
|
|
any_new[t] = 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
srec = nsrec; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -1603,12 +1768,20 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux |
|
|
|
srec = tmsg->srec; |
|
|
|
srec = tmsg->srec; |
|
|
|
if (srec) { |
|
|
|
if (srec) { |
|
|
|
if (srec->status & S_SKIPPED) { |
|
|
|
if (srec->status & S_SKIPPED) { |
|
|
|
// The message was skipped due to being too big.
|
|
|
|
// Pre-1.4 legacy only: The message was skipped due to being too big.
|
|
|
|
// We must have already seen the UID, but we might have been interrupted.
|
|
|
|
// We must have already seen the UID, but we might have been interrupted.
|
|
|
|
if (svars->maxuid[1-t] < tmsg->uid) |
|
|
|
if (svars->maxuid[1-t] < tmsg->uid) |
|
|
|
svars->maxuid[1-t] = tmsg->uid; |
|
|
|
svars->maxuid[1-t] = tmsg->uid; |
|
|
|
if (!(svars->chan->ops[t] & OP_RENEW)) |
|
|
|
if (!(svars->chan->ops[t] & OP_RENEW)) |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
|
|
|
|
srec->status = S_PENDING; |
|
|
|
|
|
|
|
// The message size was not queried, so this won't be dummified below.
|
|
|
|
|
|
|
|
if (!(tmsg->flags & F_FLAGGED)) { |
|
|
|
|
|
|
|
srec->status |= S_DUMMY(t); |
|
|
|
|
|
|
|
JLOG( "_ %u %u", (srec->uid[F], srec->uid[N]), "placeholder only - was previously skipped" ); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
JLOG( "~ %u %u %u", (srec->uid[F], srec->uid[N], srec->status), "was previously skipped" ); |
|
|
|
|
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
if (!(svars->chan->ops[t] & OP_NEW)) |
|
|
|
if (!(svars->chan->ops[t] & OP_NEW)) |
|
|
|
continue; |
|
|
|
continue; |
|
|
@ -1623,8 +1796,8 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux |
|
|
|
if (!(srec->status & S_PENDING)) |
|
|
|
if (!(srec->status & S_PENDING)) |
|
|
|
continue; // Nothing to do - the message is paired or expired
|
|
|
|
continue; // Nothing to do - the message is paired or expired
|
|
|
|
// Propagation was scheduled, but we got interrupted
|
|
|
|
// Propagation was scheduled, but we got interrupted
|
|
|
|
|
|
|
|
debug( "unpropagated old message %u\n", tmsg->uid ); |
|
|
|
} |
|
|
|
} |
|
|
|
debug( "unpropagated old message %u\n", tmsg->uid ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((svars->chan->ops[t] & OP_EXPUNGE) && (tmsg->flags & F_DELETED)) { |
|
|
|
if ((svars->chan->ops[t] & OP_EXPUNGE) && (tmsg->flags & F_DELETED)) { |
|
|
|
JLOG( "- %u %u", (srec->uid[F], srec->uid[N]), "killing - would be expunged anyway" ); |
|
|
|
JLOG( "- %u %u", (srec->uid[F], srec->uid[N]), "killing - would be expunged anyway" ); |
|
|
@ -1660,20 +1833,12 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux |
|
|
|
tmsg->srec = srec; |
|
|
|
tmsg->srec = srec; |
|
|
|
JLOG( "+ %u %u", (srec->uid[F], srec->uid[N]), "fresh" ); |
|
|
|
JLOG( "+ %u %u", (srec->uid[F], srec->uid[N]), "fresh" ); |
|
|
|
} |
|
|
|
} |
|
|
|
if ((tmsg->flags & F_FLAGGED) || tmsg->size <= svars->chan->stores[t]->max_size) { |
|
|
|
if (!(tmsg->flags & F_FLAGGED) && tmsg->size > svars->chan->stores[t]->max_size && |
|
|
|
if (srec->status != S_PENDING) { |
|
|
|
!(srec->wstate & W_UPGRADE) && !(srec->status & (S_DUMMY(F)|S_DUMMY(N)))) { |
|
|
|
srec->status = S_PENDING; |
|
|
|
srec->status |= S_DUMMY(t); |
|
|
|
JLOG( "~ %u %u %u", (srec->uid[F], srec->uid[N], srec->status), "was too big" ); |
|
|
|
JLOG( "_ %u %u", (srec->uid[F], srec->uid[N]), "placeholder only - too big" ); |
|
|
|
} |
|
|
|
|
|
|
|
any_new[t] = 1; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if (srec->status == S_SKIPPED) { |
|
|
|
|
|
|
|
debug( "-> still too big\n" ); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
srec->status = S_SKIPPED; |
|
|
|
|
|
|
|
JLOG( "~ %u %u %u", (srec->uid[F], srec->uid[N], srec->status), "skipping - too big" ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
any_new[t] = 1; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -1790,14 +1955,22 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux |
|
|
|
|
|
|
|
|
|
|
|
debug( "synchronizing flags\n" ); |
|
|
|
debug( "synchronizing flags\n" ); |
|
|
|
for (srec = svars->srecs; srec; srec = srec->next) { |
|
|
|
for (srec = svars->srecs; srec; srec = srec->next) { |
|
|
|
if ((srec->status & S_DEAD) || !srec->uid[F] || !srec->uid[N]) |
|
|
|
if (srec->status & S_DEAD) |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
for (t = 0; t < 2; t++) { |
|
|
|
for (t = 0; t < 2; t++) { |
|
|
|
|
|
|
|
if (!srec->uid[t]) |
|
|
|
|
|
|
|
continue; |
|
|
|
aflags = srec->aflags[t]; |
|
|
|
aflags = srec->aflags[t]; |
|
|
|
dflags = srec->dflags[t]; |
|
|
|
dflags = srec->dflags[t]; |
|
|
|
if (srec->wstate & W_DELETE) { |
|
|
|
if (srec->wstate & (W_DELETE|W_PURGE)) { |
|
|
|
if (!aflags) { |
|
|
|
if (!aflags) { |
|
|
|
/* This deletion propagation goes the other way round. */ |
|
|
|
// This deletion propagation goes the other way round, or
|
|
|
|
|
|
|
|
// this deletion of a dummy happens on the other side.
|
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (!srec->msg[t] && (svars->opts[t] & OPEN_OLD)) { |
|
|
|
|
|
|
|
// The message disappeared. This can happen, because the wstate may
|
|
|
|
|
|
|
|
// come from the journal, and things could have happened meanwhile.
|
|
|
|
continue; |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -1874,7 +2047,7 @@ msg_copied( int sts, uint uid, copy_vars_t *vars ) |
|
|
|
sync_rec_t *srec = vars->srec; |
|
|
|
sync_rec_t *srec = vars->srec; |
|
|
|
switch (sts) { |
|
|
|
switch (sts) { |
|
|
|
case SYNC_OK: |
|
|
|
case SYNC_OK: |
|
|
|
if (vars->msg->flags != srec->flags) { |
|
|
|
if (!(srec->wstate & W_UPGRADE) && vars->msg->flags != srec->flags) { |
|
|
|
srec->flags = vars->msg->flags; |
|
|
|
srec->flags = vars->msg->flags; |
|
|
|
JLOG( "* %u %u %u", (srec->uid[F], srec->uid[N], srec->flags), "%sed with flags", str_hl[t] ); |
|
|
|
JLOG( "* %u %u %u", (srec->uid[F], srec->uid[N], srec->flags), "%sed with flags", str_hl[t] ); |
|
|
|
} |
|
|
|
} |
|
|
@ -1937,6 +2110,7 @@ msgs_copied( sync_vars_t *svars, int t ) |
|
|
|
cv->aux = AUX; |
|
|
|
cv->aux = AUX; |
|
|
|
cv->srec = srec; |
|
|
|
cv->srec = srec; |
|
|
|
cv->msg = tmsg; |
|
|
|
cv->msg = tmsg; |
|
|
|
|
|
|
|
cv->minimal = (srec->status & S_DUMMY(t)); |
|
|
|
copy_msg( cv ); |
|
|
|
copy_msg( cv ); |
|
|
|
svars->state[t] &= ~ST_SENDING_NEW; |
|
|
|
svars->state[t] &= ~ST_SENDING_NEW; |
|
|
|
if (check_cancel( svars )) |
|
|
|
if (check_cancel( svars )) |
|
|
@ -2085,6 +2259,7 @@ msgs_flags_set( sync_vars_t *svars, int t ) |
|
|
|
cv->aux = INV_AUX; |
|
|
|
cv->aux = INV_AUX; |
|
|
|
cv->srec = NULL; |
|
|
|
cv->srec = NULL; |
|
|
|
cv->msg = tmsg; |
|
|
|
cv->msg = tmsg; |
|
|
|
|
|
|
|
cv->minimal = 0; |
|
|
|
copy_msg( cv ); |
|
|
|
copy_msg( cv ); |
|
|
|
if (check_cancel( svars )) |
|
|
|
if (check_cancel( svars )) |
|
|
|
goto out; |
|
|
|
goto out; |
|
|
|