diff --git a/src/config.c b/src/config.c index d8f9b52..878440b 100644 --- a/src/config.c +++ b/src/config.c @@ -35,7 +35,7 @@ store_conf_t *stores; channel_conf_t *channels; group_conf_t *groups; -int global_mops, global_sops; +int global_ops[2]; char *global_sync_state; int @@ -92,7 +92,7 @@ parse_size( conffile_t *cfile ) } static int -getopt_helper( conffile_t *cfile, int *cops, int *mops, int *sops, char **sync_state ) +getopt_helper( conffile_t *cfile, int *cops, int ops[], char **sync_state ) { char *arg; @@ -112,56 +112,56 @@ getopt_helper( conffile_t *cfile, int *cops, int *mops, int *sops, char **sync_s else if (!strcasecmp( "Flags", arg )) *cops |= OP_FLAGS; else if (!strcasecmp( "PullReNew", arg )) - *sops |= OP_RENEW; + ops[S] |= OP_RENEW; else if (!strcasecmp( "PullNew", arg )) - *sops |= OP_NEW; + ops[S] |= OP_NEW; else if (!strcasecmp( "PullDelete", arg )) - *sops |= OP_DELETE; + ops[S] |= OP_DELETE; else if (!strcasecmp( "PullFlags", arg )) - *sops |= OP_FLAGS; + ops[S] |= OP_FLAGS; else if (!strcasecmp( "PushReNew", arg )) - *mops |= OP_RENEW; + ops[M] |= OP_RENEW; else if (!strcasecmp( "PushNew", arg )) - *mops |= OP_NEW; + ops[M] |= OP_NEW; else if (!strcasecmp( "PushDelete", arg )) - *mops |= OP_DELETE; + ops[M] |= OP_DELETE; else if (!strcasecmp( "PushFlags", arg )) - *mops |= OP_FLAGS; + ops[M] |= OP_FLAGS; else if (!strcasecmp( "All", arg ) || !strcasecmp( "Full", arg )) *cops |= XOP_PULL|XOP_PUSH; else if (strcasecmp( "None", arg ) && strcasecmp( "Noop", arg )) fprintf( stderr, "%s:%d: invalid Sync arg '%s'\n", cfile->file, cfile->line, arg ); while ((arg = next_arg( &cfile->rest ))); - *mops |= XOP_HAVE_TYPE; + ops[M] |= XOP_HAVE_TYPE; } else if (!strcasecmp( "Expunge", cfile->cmd )) { arg = cfile->val; do if (!strcasecmp( "Both", arg )) *cops |= OP_EXPUNGE; else if (!strcasecmp( "Master", arg )) - *mops |= OP_EXPUNGE; + ops[M] |= OP_EXPUNGE; else if (!strcasecmp( "Slave", arg )) - *sops |= OP_EXPUNGE; + ops[S] |= OP_EXPUNGE; else if (strcasecmp( "None", arg )) fprintf( stderr, "%s:%d: invalid Expunge arg '%s'\n", cfile->file, cfile->line, arg ); while ((arg = next_arg( &cfile->rest ))); - *mops |= XOP_HAVE_EXPUNGE; + ops[M] |= XOP_HAVE_EXPUNGE; } else if (!strcasecmp( "Create", cfile->cmd )) { arg = cfile->val; do if (!strcasecmp( "Both", arg )) *cops |= OP_CREATE; else if (!strcasecmp( "Master", arg )) - *mops |= OP_CREATE; + ops[M] |= OP_CREATE; else if (!strcasecmp( "Slave", arg )) - *sops |= OP_CREATE; + ops[S] |= OP_CREATE; else if (strcasecmp( "None", arg )) fprintf( stderr, "%s:%d: invalid Create arg '%s'\n", cfile->file, cfile->line, arg ); while ((arg = next_arg( &cfile->rest ))); - *mops |= XOP_HAVE_CREATE; + ops[M] |= XOP_HAVE_CREATE; } else if (!strcasecmp( "SyncState", cfile->cmd )) *sync_state = expand_strdup( cfile->val ); else @@ -194,29 +194,29 @@ getcline( conffile_t *cfile ) /* XXX - this does not detect None conflicts ... */ int -merge_ops( int cops, int *mops, int *sops ) +merge_ops( int cops, int ops[] ) { int aops; - aops = *mops | *sops; - if (*mops & XOP_HAVE_TYPE) { + aops = ops[M] | ops[S]; + if (ops[M] & XOP_HAVE_TYPE) { if (aops & OP_MASK_TYPE) { if (aops & cops & OP_MASK_TYPE) { cfl: fprintf( stderr, "Conflicting Sync args specified.\n" ); return 1; } - *mops |= cops & OP_MASK_TYPE; - *sops |= cops & OP_MASK_TYPE; + ops[M] |= cops & OP_MASK_TYPE; + ops[S] |= cops & OP_MASK_TYPE; if (cops & XOP_PULL) { - if (*sops & OP_MASK_TYPE) + if (ops[S] & OP_MASK_TYPE) goto cfl; - *sops |= OP_MASK_TYPE; + ops[S] |= OP_MASK_TYPE; } if (cops & XOP_PUSH) { - if (*mops & OP_MASK_TYPE) + if (ops[M] & OP_MASK_TYPE) goto cfl; - *mops |= OP_MASK_TYPE; + ops[M] |= OP_MASK_TYPE; } } else if (cops & (OP_MASK_TYPE|XOP_MASK_DIR)) { if (!(cops & OP_MASK_TYPE)) @@ -224,26 +224,26 @@ merge_ops( int cops, int *mops, int *sops ) else if (!(cops & XOP_MASK_DIR)) cops |= XOP_PULL|XOP_PUSH; if (cops & XOP_PULL) - *sops |= cops & OP_MASK_TYPE; + ops[S] |= cops & OP_MASK_TYPE; if (cops & XOP_PUSH) - *mops |= cops & OP_MASK_TYPE; + ops[M] |= cops & OP_MASK_TYPE; } } - if (*mops & XOP_HAVE_EXPUNGE) { + if (ops[M] & XOP_HAVE_EXPUNGE) { if (aops & cops & OP_EXPUNGE) { fprintf( stderr, "Conflicting Expunge args specified.\n" ); return 1; } - *mops |= cops & OP_EXPUNGE; - *sops |= cops & OP_EXPUNGE; + ops[M] |= cops & OP_EXPUNGE; + ops[S] |= cops & OP_EXPUNGE; } - if (*mops & XOP_HAVE_CREATE) { + if (ops[M] & XOP_HAVE_CREATE) { if (aops & cops & OP_CREATE) { fprintf( stderr, "Conflicting Create args specified.\n" ); return 1; } - *mops |= cops & OP_CREATE; - *sops |= cops & OP_CREATE; + ops[M] |= cops & OP_CREATE; + ops[S] |= cops & OP_CREATE; } return 0; } @@ -252,12 +252,12 @@ int load_config( const char *where, int pseudo ) { conffile_t cfile; - store_conf_t *store, **storeapp = &stores, **sptarg; + store_conf_t *store, **storeapp = &stores; channel_conf_t *channel, **channelapp = &channels; group_conf_t *group, **groupapp = &groups; string_list_t *chanlist, **chanlistapp; - char *arg, *p, **ntarg; - int err, len, cops, gcops, max_size; + char *arg, *p; + int err, len, cops, gcops, max_size, ms; char path[_POSIX_PATH_MAX]; char buf[1024]; @@ -315,12 +315,10 @@ load_config( const char *where, int pseudo ) while ((arg = next_arg( &cfile.rest ))); } else if (!strcasecmp( "Master", cfile.cmd )) { - sptarg = &channel->master; - ntarg = &channel->master_name; + ms = M; goto linkst; } else if (!strcasecmp( "Slave", cfile.cmd )) { - sptarg = &channel->slave; - ntarg = &channel->slave_name; + ms = S; linkst: if (*cfile.val != ':' || !(p = strchr( cfile.val + 1, ':' ))) { fprintf( stderr, "%s:%d: malformed mailbox spec\n", @@ -331,7 +329,7 @@ load_config( const char *where, int pseudo ) *p = 0; for (store = stores; store; store = store->next) if (!strcmp( store->name, cfile.val + 1 )) { - *sptarg = store; + channel->stores[ms] = store; goto stpcom; } fprintf( stderr, "%s:%d: unknown store '%s'\n", @@ -340,24 +338,24 @@ load_config( const char *where, int pseudo ) continue; stpcom: if (*++p) - *ntarg = nfstrdup( p ); - } else if (!getopt_helper( &cfile, &cops, &channel->mops, &channel->sops, &channel->sync_state )) { + channel->boxes[ms] = nfstrdup( p ); + } else if (!getopt_helper( &cfile, &cops, channel->ops, &channel->sync_state )) { fprintf( stderr, "%s:%d: unknown keyword '%s'\n", cfile.file, cfile.line, cfile.cmd ); err = 1; } } - if (!channel->master) { + if (!channel->stores[M]) { fprintf( stderr, "channel '%s' refers to no master store\n", channel->name ); err = 1; - } else if (!channel->slave) { + } else if (!channel->stores[S]) { fprintf( stderr, "channel '%s' refers to no slave store\n", channel->name ); err = 1; - } else if (merge_ops( cops, &channel->mops, &channel->sops )) + } else if (merge_ops( cops, channel->ops )) err = 1; else { if (max_size >= 0) - channel->master->max_size = channel->slave->max_size = max_size; + channel->stores[M]->max_size = channel->stores[S]->max_size = max_size; *channelapp = channel; channelapp = &channel->next; } @@ -400,7 +398,7 @@ load_config( const char *where, int pseudo ) } break; } - else if (!getopt_helper( &cfile, &gcops, &global_mops, &global_sops, &global_sync_state )) + else if (!getopt_helper( &cfile, &gcops, global_ops, &global_sync_state )) { fprintf( stderr, "%s:%d: unknown section keyword '%s'\n", cfile.file, cfile.line, cfile.cmd ); @@ -412,7 +410,7 @@ load_config( const char *where, int pseudo ) } } fclose (cfile.fp); - err |= merge_ops( gcops, &global_mops, &global_sops ); + err |= merge_ops( gcops, global_ops ); if (!global_sync_state) global_sync_state = expand_strdup( "~/." EXE "/" ); if (!err && pseudo) diff --git a/src/isync.h b/src/isync.h index 147702b..6a70b07 100644 --- a/src/isync.h +++ b/src/isync.h @@ -73,8 +73,8 @@ typedef struct store_conf { char *name; driver_t *driver; const char *path; /* should this be here? its interpretation is driver-specific */ - char *map_inbox; - char *trash; + const char *map_inbox; + const char *trash; unsigned max_size; /* off_t is overkill */ unsigned trash_remote_new:1, trash_only_new:1; } store_conf_t; @@ -84,20 +84,23 @@ typedef struct string_list { char string[1]; } string_list_t; +#define M 0 /* master */ +#define S 1 /* slave */ + typedef struct channel_conf { struct channel_conf *next; - char *name; - store_conf_t *master, *slave; - char *master_name, *slave_name; + const char *name; + store_conf_t *stores[2]; + const char *boxes[2]; char *sync_state; string_list_t *patterns; - int mops, sops; + int ops[2]; unsigned max_messages; /* for slave only */ } channel_conf_t; typedef struct group_conf { struct group_conf *next; - char *name; + const char *name; string_list_t *channels; } group_conf_t; @@ -222,27 +225,24 @@ unsigned char arc4_getbyte( void ); /* sync.c */ -#define SYNC_OK 0 -#define SYNC_FAIL 1 -#define SYNC_MASTER_BAD 2 -#define SYNC_SLAVE_BAD 3 +#define SYNC_OK 0 +#define SYNC_FAIL 1 +#define SYNC_BAD(ms) (2+(ms)) -int sync_boxes( store_t *, const char *, - store_t *, const char *, - channel_conf_t * ); +int sync_boxes( store_t *ctx[], const char *names[], channel_conf_t * ); /* config.c */ extern channel_conf_t *channels; extern group_conf_t *groups; -extern int global_mops, global_sops; +extern int global_ops[2]; extern char *global_sync_state; int parse_bool( conffile_t *cfile ); int parse_int( conffile_t *cfile ); int parse_size( conffile_t *cfile ); int getcline( conffile_t *cfile ); -int merge_ops( int cops, int *mops, int *sops ); +int merge_ops( int cops, int ops[] ); int load_config( const char *filename, int pseudo ); void parse_generic_store( store_conf_t *store, conffile_t *cfg, int *err ); diff --git a/src/main.c b/src/main.c index 4988f6c..da12794 100644 --- a/src/main.c +++ b/src/main.c @@ -137,20 +137,20 @@ filter_boxes( string_list_t *boxes, string_list_t *patterns ) } static void -merge_actions( channel_conf_t *chan, int mops, int sops, int have, int mask, int def ) +merge_actions( channel_conf_t *chan, int ops[], int have, int mask, int def ) { - if (mops & have) { - chan->mops &= ~mask; - chan->mops |= mops & mask; - chan->sops &= ~mask; - chan->sops |= sops & mask; - } else if (!(chan->mops & have)) { - if (global_mops & have) { - chan->mops |= global_mops & mask; - chan->sops |= global_sops & mask; + if (ops[M] & have) { + chan->ops[M] &= ~mask; + chan->ops[M] |= ops[M] & mask; + chan->ops[S] &= ~mask; + chan->ops[S] |= ops[S] & mask; + } else if (!(chan->ops[M] & have)) { + if (global_ops[M] & have) { + chan->ops[M] |= global_ops[M] & mask; + chan->ops[S] |= global_ops[S] & mask; } else { - chan->mops |= def; - chan->sops |= def; + chan->ops[M] |= def; + chan->ops[S] |= def; } } } @@ -159,14 +159,15 @@ int main( int argc, char **argv ) { channel_conf_t *chan; - store_conf_t *mconf, *sconf; + store_conf_t *conf[2]; group_conf_t *group; - driver_t *mdriver, *sdriver; - store_t *mctx, *sctx; - string_list_t *umboxes, *usboxes, *mboxes, *sboxes, *mbox, *sbox, **mboxp, **sboxp, *cboxes, *chanptr; + driver_t *driver[2]; + store_t *ctx[2]; + string_list_t *uboxes[2], *boxes[2], *mbox, *sbox, **mboxp, **sboxp, *cboxes, *chanptr; char *config = 0, *channame, *boxlist, *opt, *ochar; - int all = 0, list = 0, cops = 0, mops = 0, sops = 0, gumboxes, gusboxes; - int oind, ret, op, multiple, pseudo = 0; + const char *names[2]; + int all = 0, list = 0, cops = 0, ops[2] = { 0, 0 }, guboxes[2]; + int oind, ret, op, multiple, pseudo = 0, t; gethostname( Hostname, sizeof(Hostname) ); if ((ochar = strchr( Hostname, '.' ))) @@ -213,9 +214,9 @@ main( int argc, char **argv ) if (!Quiet) Quiet = 1; } else if (!strcmp( opt, "pull" )) - cops |= XOP_PULL, mops |= XOP_HAVE_TYPE; + cops |= XOP_PULL, ops[M] |= XOP_HAVE_TYPE; else if (!strcmp( opt, "push" )) - cops |= XOP_PUSH, mops |= XOP_HAVE_TYPE; + cops |= XOP_PUSH, ops[M] |= XOP_HAVE_TYPE; else if (!memcmp( opt, "create", 6 )) { opt += 6; op = OP_CREATE|XOP_HAVE_CREATE; @@ -223,24 +224,24 @@ main( int argc, char **argv ) if (!*opt) cops |= op; else if (!strcmp( opt, "-master" )) - mops |= op, ochar++; + ops[M] |= op, ochar++; else if (!strcmp( opt, "-slave" )) - sops |= op, ochar++; + ops[S] |= op, ochar++; else goto badopt; - mops |= op & (XOP_HAVE_CREATE|XOP_HAVE_EXPUNGE); + ops[M] |= op & (XOP_HAVE_CREATE|XOP_HAVE_EXPUNGE); } else if (!memcmp( opt, "expunge", 7 )) { opt += 7; op = OP_EXPUNGE|XOP_HAVE_EXPUNGE; goto lcop; } else if (!strcmp( opt, "no-expunge" )) - mops |= XOP_HAVE_EXPUNGE; + ops[M] |= XOP_HAVE_EXPUNGE; else if (!strcmp( opt, "no-create" )) - mops |= XOP_HAVE_CREATE; + ops[M] |= XOP_HAVE_CREATE; else if (!strcmp( opt, "full" )) - mops |= XOP_HAVE_TYPE|XOP_PULL|XOP_PUSH; + ops[M] |= XOP_HAVE_TYPE|XOP_PULL|XOP_PUSH; else if (!strcmp( opt, "noop" )) - mops |= XOP_HAVE_TYPE; + ops[M] |= XOP_HAVE_TYPE; else if (!memcmp( opt, "pull", 4 )) { op = XOP_PULL; lcac: @@ -272,11 +273,11 @@ main( int argc, char **argv ) return 1; } switch (op & XOP_MASK_DIR) { - case XOP_PULL: sops |= op & OP_MASK_TYPE; break; - case XOP_PUSH: mops |= op & OP_MASK_TYPE; break; + case XOP_PULL: ops[S] |= op & OP_MASK_TYPE; break; + case XOP_PUSH: ops[M] |= op & OP_MASK_TYPE; break; default: cops |= op; break; } - mops |= XOP_HAVE_TYPE; + ops[M] |= XOP_HAVE_TYPE; } continue; } @@ -308,14 +309,14 @@ main( int argc, char **argv ) op = OP_CREATE|XOP_HAVE_CREATE; cop: if (*ochar == 'm') - mops |= op, ochar++; + ops[M] |= op, ochar++; else if (*ochar == 's') - sops |= op, ochar++; + ops[S] |= op, ochar++; else if (*ochar == '-') ochar++; else cops |= op; - mops |= op & (XOP_HAVE_CREATE|XOP_HAVE_EXPUNGE); + ops[M] |= op & (XOP_HAVE_CREATE|XOP_HAVE_EXPUNGE); break; case 'X': op = OP_EXPUNGE|XOP_HAVE_EXPUNGE; @@ -323,7 +324,7 @@ main( int argc, char **argv ) case 'F': cops |= XOP_PULL|XOP_PUSH; case '0': - mops |= XOP_HAVE_TYPE; + ops[M] |= XOP_HAVE_TYPE; break; case 'n': case 'd': @@ -346,13 +347,13 @@ main( int argc, char **argv ) } if (op & OP_MASK_TYPE) switch (op & XOP_MASK_DIR) { - case XOP_PULL: sops |= op & OP_MASK_TYPE; break; - case XOP_PUSH: mops |= op & OP_MASK_TYPE; break; + case XOP_PULL: ops[S] |= op & OP_MASK_TYPE; break; + case XOP_PUSH: ops[M] |= op & OP_MASK_TYPE; break; default: cops |= op; break; } else cops |= op; - mops |= XOP_HAVE_TYPE; + ops[M] |= XOP_HAVE_TYPE; break; case 'L': op = XOP_PULL; @@ -383,7 +384,7 @@ main( int argc, char **argv ) } } - if (merge_ops( cops, &mops, &sops )) + if (merge_ops( cops, ops )) return 1; if (load_config( config, pseudo )) @@ -401,11 +402,11 @@ main( int argc, char **argv ) ret = 0; chan = channels; chanptr = 0; - mctx = sctx = 0; - mconf = sconf = 0; /* make-gcc-happy */ - mdriver = sdriver = 0; /* make-gcc-happy */ - gumboxes = gusboxes = 0; - umboxes = usboxes = 0; + ctx[M] = ctx[S] = 0; + conf[M] = conf[S] = 0; /* make-gcc-happy */ + driver[M] = driver[S] = 0; /* make-gcc-happy */ + guboxes[M] = guboxes[S] = 0; + uboxes[M] = uboxes[S] = 0; if (all) multiple = channels->next != 0; else if (argv[oind + 1]) @@ -443,48 +444,31 @@ main( int argc, char **argv ) goto gotnone; gotchan: ; } - merge_actions( chan, mops, sops, XOP_HAVE_TYPE, OP_MASK_TYPE, OP_MASK_TYPE ); - merge_actions( chan, mops, sops, XOP_HAVE_CREATE, OP_CREATE, 0 ); - merge_actions( chan, mops, sops, XOP_HAVE_EXPUNGE, OP_EXPUNGE, 0 ); + merge_actions( chan, ops, XOP_HAVE_TYPE, OP_MASK_TYPE, OP_MASK_TYPE ); + merge_actions( chan, ops, XOP_HAVE_CREATE, OP_CREATE, 0 ); + merge_actions( chan, ops, XOP_HAVE_EXPUNGE, OP_EXPUNGE, 0 ); - mboxes = sboxes = cboxes = 0; + boxes[M] = boxes[S] = cboxes = 0; /* possible todo: handle master <-> slave swaps */ - if (mctx) { - if (mconf == chan->master) - goto gotmctx; - free_string_list( umboxes ); - umboxes = 0; - gumboxes = 0; - if (mconf->driver != chan->master->driver) { - mdriver->close_store( mctx ); - mctx = 0; + for (t = 0; t < 2; t++) { + if (ctx[t]) { + if (conf[t] == chan->stores[t]) + continue; + free_string_list( uboxes[t] ); + uboxes[t] = 0; + guboxes[t] = 0; + if (conf[t]->driver != chan->stores[t]->driver) { + driver[t]->close_store( ctx[t] ); + ctx[t] = 0; + } + } + conf[t] = chan->stores[t]; + driver[t] = conf[t]->driver; + if (!(ctx[t] = driver[t]->open_store( chan->stores[t], ctx[t] ))) { + ret = 1; + goto next; } } - mconf = chan->master; - mdriver = mconf->driver; - if (!(mctx = mdriver->open_store( chan->master, mctx ))) { - ret = 1; - goto next; - } - gotmctx: - if (sctx) { - if (sconf == chan->slave) - goto gotsctx; - free_string_list( usboxes ); - usboxes = 0; - gusboxes = 0; - if (sconf->driver != chan->slave->driver) { - sdriver->close_store( sctx ); - sctx = 0; - } - } - sconf = chan->slave; - sdriver = sconf->driver; - if (!(sctx = sdriver->open_store( chan->slave, sctx ))) { - ret = 1; - goto next; - } - gotsctx: info( "Channel %s\n", chan->name ); if (list && multiple) printf( "%s:\n", chan->name ); @@ -492,49 +476,36 @@ main( int argc, char **argv ) for (boxlist = strtok( boxlist, ",\n" ); boxlist; boxlist = strtok( 0, ",\n" )) if (list) puts( boxlist ); - else - switch (sync_boxes( mctx, boxlist, sctx, boxlist, chan )) { - case SYNC_MASTER_BAD: goto screwm; - case SYNC_SLAVE_BAD: goto screws; + else { + names[M] = names[S] = boxlist; + switch (sync_boxes( ctx, names, chan )) { + case SYNC_BAD(M): t = M; goto screwt; + case SYNC_BAD(S): t = S; goto screwt; case SYNC_FAIL: ret = 1; } + } } else if (chan->patterns) { - if (!gumboxes) { - if (mdriver->list( mctx, &umboxes ) != DRV_OK) { - screwm: - mdriver->close_store( mctx ); - free_string_list( umboxes ); - umboxes = 0; - gumboxes = 0; - mctx = 0; - ret = 1; - goto next; - } else { - gumboxes = 1; - if (mctx->conf->map_inbox) - add_string_list( &umboxes, mctx->conf->map_inbox ); + for (t = 0; t < 2; t++) { + if (!guboxes[t]) { + if (driver[t]->list( ctx[t], &uboxes[t] ) != DRV_OK) { + screwt: + driver[t]->close_store( ctx[t] ); + free_string_list( uboxes[t] ); + uboxes[t] = 0; + guboxes[t] = 0; + ctx[t] = 0; + ret = 1; + goto next; + } else { + guboxes[t] = 1; + if (ctx[t]->conf->map_inbox) + add_string_list( &uboxes[t], ctx[t]->conf->map_inbox ); + } } + boxes[t] = filter_boxes( uboxes[t], chan->patterns ); } - if (!gusboxes) { - if (sdriver->list( sctx, &usboxes ) != DRV_OK) { - screws: - sdriver->close_store( sctx ); - free_string_list( usboxes ); - usboxes = 0; - gusboxes = 0; - sctx = 0; - ret = 1; - goto next; - } else { - gusboxes = 1; - if (sctx->conf->map_inbox) - add_string_list( &usboxes, sctx->conf->map_inbox ); - } - } - mboxes = filter_boxes( umboxes, chan->patterns ); - sboxes = filter_boxes( usboxes, chan->patterns ); - for (mboxp = &mboxes; (mbox = *mboxp); ) { - for (sboxp = &sboxes; (sbox = *sboxp); sboxp = &sbox->next) + for (mboxp = &boxes[M]; (mbox = *mboxp); ) { + for (sboxp = &boxes[S]; (sbox = *sboxp); sboxp = &sbox->next) if (!strcmp( sbox->string, mbox->string )) { *sboxp = sbox->next; free( sbox ); @@ -549,48 +520,42 @@ main( int argc, char **argv ) for (mbox = cboxes; mbox; mbox = mbox->next) if (list) puts( mbox->string ); - else - switch (sync_boxes( mctx, mbox->string, sctx, mbox->string, chan )) { - case SYNC_MASTER_BAD: goto screwm; - case SYNC_SLAVE_BAD: goto screws; + else { + names[M] = names[S] = mbox->string; + switch (sync_boxes( ctx, names, chan )) { + case SYNC_BAD(M): t = M; goto screwt; + case SYNC_BAD(S): t = S; goto screwt; case SYNC_FAIL: ret = 1; } - if ((chan->sops & OP_MASK_TYPE) && (chan->sops & OP_CREATE)) { - for (mbox = mboxes; mbox; mbox = mbox->next) - if (list) - puts( mbox->string ); - else - switch (sync_boxes( mctx, mbox->string, sctx, mbox->string, chan )) { - case SYNC_MASTER_BAD: goto screwm; - case SYNC_SLAVE_BAD: goto screws; - case SYNC_FAIL: ret = 1; + } + for (t = 0; t < 2; t++) + if ((chan->ops[t] & OP_MASK_TYPE) && (chan->ops[t] & OP_CREATE)) { + for (mbox = boxes[t]; mbox; mbox = mbox->next) + if (list) + puts( mbox->string ); + else { + names[M] = names[S] = mbox->string; + switch (sync_boxes( ctx, names, chan )) { + case SYNC_BAD(M): t = M; goto screwt; + case SYNC_BAD(S): t = S; goto screwt; + case SYNC_FAIL: ret = 1; + } } - } - if ((chan->mops & OP_MASK_TYPE) && (chan->mops & OP_CREATE)) { - for (mbox = sboxes; mbox; mbox = mbox->next) - if (list) - puts( mbox->string ); - else - switch (sync_boxes( mctx, mbox->string, sctx, mbox->string, chan )) { - case SYNC_MASTER_BAD: goto screwm; - case SYNC_SLAVE_BAD: goto screws; - case SYNC_FAIL: ret = 1; - } - } + } } else if (list) - printf( "%s <=> %s\n", chan->master_name, chan->slave_name ); + printf( "%s <=> %s\n", chan->boxes[M], chan->boxes[S] ); else - switch (sync_boxes( mctx, chan->master_name, sctx, chan->slave_name, chan )) { - case SYNC_MASTER_BAD: goto screwm; - case SYNC_SLAVE_BAD: goto screws; + switch (sync_boxes( ctx, chan->boxes, chan )) { + case SYNC_BAD(M): t = M; goto screwt; + case SYNC_BAD(S): t = S; goto screwt; case SYNC_FAIL: ret = 1; } next: free_string_list( cboxes ); - free_string_list( mboxes ); - free_string_list( sboxes ); + free_string_list( boxes[M] ); + free_string_list( boxes[S] ); if (all) { if (!(chan = chan->next)) break; @@ -602,12 +567,12 @@ main( int argc, char **argv ) break; } } - free_string_list( usboxes ); - if (sctx) - sdriver->close_store( sctx ); - free_string_list( umboxes ); - if (mctx) - mdriver->close_store( mctx ); + free_string_list( uboxes[S] ); + if (ctx[S]) + driver[S]->close_store( ctx[S] ); + free_string_list( uboxes[M] ); + if (ctx[M]) + driver[M]->close_store( ctx[M] ); return ret; } diff --git a/src/sync.c b/src/sync.c index 2b80787..9eeffd3 100644 --- a/src/sync.c +++ b/src/sync.c @@ -33,6 +33,8 @@ #include #include +static const char *str_ms[] = { "master", "slave" }, *str_hl[] = { "push", "pull" }; + void Fprintf( FILE *f, const char *msg, ... ) { @@ -76,40 +78,6 @@ make_flags( int flags, char *buf ) return d; } -static void -makeopts( int dops, store_conf_t *dconf, int *dopts, - store_conf_t *sconf, int *sopts ) -{ - if (dops & (OP_DELETE|OP_FLAGS)) { - *dopts |= OPEN_SETFLAGS; - *sopts |= OPEN_OLD; - if (dops & OP_FLAGS) - *sopts |= OPEN_FLAGS; - } - if (dops & (OP_NEW|OP_RENEW)) { - *dopts |= OPEN_APPEND; - if (dops & OP_RENEW) - *sopts |= OPEN_OLD; - if (dops & OP_NEW) - *sopts |= OPEN_NEW; - if (dops & OP_EXPUNGE) - *sopts |= OPEN_FLAGS; - if (dconf->max_size) - *sopts |= OPEN_SIZE; - } - if (dops & OP_EXPUNGE) { - *dopts |= OPEN_EXPUNGE; - if (dconf->trash) { - if (!dconf->trash_only_new) - *dopts |= OPEN_OLD; - *dopts |= OPEN_NEW|OPEN_FLAGS; - } else if (sconf->trash && sconf->trash_remote_new) - *dopts |= OPEN_NEW|OPEN_FLAGS; - } - if (dops & OP_CREATE) - *dopts |= OPEN_CREATE; -} - static void dump_box( store_t *ctx ) { @@ -154,287 +122,43 @@ findmsg( store_t *ctx, int uid, message_t **nmsg, const char *who ) #define S_DEAD (1<<0) #define S_EXPIRED (1<<1) -#define S_DEL_MASTER (1<<2) -#define S_DEL_SLAVE (1<<3) -#define S_EXP_SLAVE (1<<4) +#define S_DEL(ms) (1<<(2+(ms))) +#define S_EXP_S (1<<4) typedef struct sync_rec { struct sync_rec *next; /* string_list_t *keywords; */ - int muid, suid; + int uid[2]; unsigned char flags, status; } sync_rec_t; -#define EX_OK 0 -#define EX_FAIL 1 -#define EX_STORE_BAD 2 -#define EX_RSTORE_BAD 3 - -static int -expunge( store_t *ctx, store_t *rctx ) -{ - driver_t *driver = ctx->conf->driver, *rdriver = rctx->conf->driver; - message_t *msg; - msg_data_t msgdata; - - for (msg = ctx->msgs; msg; msg = msg->next) - if (msg->flags & F_DELETED) { - if (ctx->conf->trash) { - if (!ctx->conf->trash_only_new || (msg->status & M_NOT_SYNCED)) { - debug( " trashing message %d\n", msg->uid ); - switch (driver->trash_msg( ctx, msg )) { - case DRV_STORE_BAD: return EX_STORE_BAD; - default: return EX_FAIL; - case DRV_OK: break; - } - } else - debug( " not trashing message %d - not new\n", msg->uid ); - } else if (rctx->conf->trash && rctx->conf->trash_remote_new) { - if (msg->status & M_NOT_SYNCED) { - if (!rctx->conf->max_size || msg->size <= rctx->conf->max_size) { - debug( " remote trashing message %d\n", msg->uid ); - msgdata.flags = msg->flags; - switch (driver->fetch_msg( ctx, msg, &msgdata )) { - case DRV_STORE_BAD: return EX_STORE_BAD; - default: return EX_FAIL; - case DRV_OK: break; - } - switch (rdriver->store_msg( rctx, &msgdata, 0 )) { - case DRV_STORE_BAD: return EX_RSTORE_BAD; - default: return EX_FAIL; - case DRV_OK: break; - } - } else - debug( " not remote trashing message %d - too big\n", msg->uid ); - } else - debug( " not remote trashing message %d - not new\n", msg->uid ); - } - } - - switch (driver->close( ctx )) { - case DRV_STORE_BAD: return EX_STORE_BAD; - default: return EX_FAIL; - case DRV_OK: return EX_OK;; - } -} - /* cases: a) both non-null b) only master null - b.1) muid 0 - b.2) muid -1 + b.1) uid[M] 0 + b.2) uid[M] -1 b.3) master not scanned b.4) master gone c) only slave null - c.1) suid 0 - c.2) suid -1 + c.1) uid[S] 0 + c.2) uid[S] -1 c.3) slave not scanned c.4) slave gone d) both null d.1) both gone - d.2) muid 0, slave not scanned - d.3) muid -1, slave not scanned + d.2) uid[M] 0, slave not scanned + d.3) uid[M] -1, slave not scanned d.4) master gone, slave not scanned - d.5) muid 0, slave gone - d.6) muid -1, slave gone - d.7) suid 0, master not scanned - d.8) suid -1, master not scanned + d.5) uid[M] 0, slave gone + d.6) uid[M] -1, slave gone + d.7) uid[S] 0, master not scanned + d.8) uid[S] -1, master not scanned d.9) slave gone, master not scanned - d.10) suid 0, master gone - d.11) suid -1, master gone - impossible cases: both muid & suid 0 or -1, both not scanned + d.10) uid[S] 0, master gone + d.11) uid[S] -1, master gone + impossible cases: both uid[M] & uid[S] 0 or -1, both not scanned */ -static int -sync_old( int tops, store_t *sctx, store_t *tctx, store_conf_t *tconf, FILE *jfp, int pull, - unsigned char *nflags, sync_rec_t *srec, message_t *smsg, message_t *tmsg, int dels, int delt ) -{ - driver_t *tdriver = tctx->conf->driver, *sdriver = sctx->conf->driver; - int uid, tuid, unex; - unsigned char sflags, aflags, dflags, rflags; - msg_data_t msgdata; - - /* excludes (push) c.3) d.2) d.3) d.4) / (pull) b.3) d.7) d.8) d.9) */ - tuid = pull ? srec->suid : srec->muid; - if (!tuid) { - /* b.1) / c.1) */ - debug( pull ? " no more slave\n" : " no more master\n" ); - } else if (dels) { - /* c.4) d.9) / b.4) d.4) */ - debug( pull ? " master vanished\n" : " slave vanished\n" ); - if (tmsg && tmsg->flags != *nflags) - info( "Info: Conflicting changes in (%d,%d)\n", srec->muid, srec->suid ); - if (tops & OP_DELETE) { - debug( pull ? " -> pulling delete\n" : " -> pushing delete\n" ); - switch (tdriver->set_flags( tctx, tmsg, tuid, F_DELETED, 0 )) { - case DRV_STORE_BAD: return pull ? SYNC_SLAVE_BAD : SYNC_MASTER_BAD; - case DRV_BOX_BAD: return SYNC_FAIL; - default: /* ok */ break; - case DRV_OK: - if (pull) { - Fprintf( jfp, "< %d %d 0\n", srec->muid, srec->suid ); - srec->muid = 0; - } else { - Fprintf( jfp, "> %d %d 0\n", srec->muid, srec->suid ); - srec->suid = 0; - } - } - } - } else if (!smsg) - /* c.1) c.2) d.7) d.8) / b.1) b.2) d.2) d.3) */ - ; - else if (tuid < 0) { - /* b.2) / c.2) */ - debug( pull ? " no slave yet\n" : " no master yet\n" ); - if (tops & OP_RENEW) { - if ((tops & OP_EXPUNGE) && (smsg->flags & F_DELETED)) { - debug( pull ? " -> not pulling - would be expunged anyway\n" : " -> not pushing - would be expunged anyway\n" ); - smsg->status |= M_NOT_SYNCED; - } else { - if ((smsg->flags & F_FLAGGED) || !tconf->max_size || smsg->size <= tconf->max_size) { - debug( pull ? " -> pulling it\n" : " -> pushing it\n" ); - msgdata.flags = smsg->flags; - switch (sdriver->fetch_msg( sctx, smsg, &msgdata )) { - case DRV_STORE_BAD: return pull ? SYNC_MASTER_BAD : SYNC_SLAVE_BAD; - case DRV_BOX_BAD: return SYNC_FAIL; - default: /* ok */ smsg->status |= M_NOT_SYNCED; break; - case DRV_OK: - smsg->flags = msgdata.flags; - switch (tdriver->store_msg( tctx, &msgdata, &uid )) { - case DRV_STORE_BAD: return pull ? SYNC_SLAVE_BAD : SYNC_MASTER_BAD; - default: return SYNC_FAIL; - case DRV_OK: - if (pull) { - srec->suid = uid; - Fprintf( jfp, "> %d -1 %d\n", srec->muid, srec->suid ); - } else { - srec->muid = uid; - Fprintf( jfp, "< -1 %d %d\n", srec->suid, srec->muid ); - } - *nflags = smsg->flags; - } - } - } else { - debug( pull ? " -> not pulling - still too big\n" : " -> not pushing - still too big\n" ); - smsg->status |= M_NOT_SYNCED; - } - } - } else - smsg->status |= M_NOT_SYNCED; - } else if (!delt) { - /* a) & b.3) / c.3) */ - debug( pull ? " may pull\n" : " may push\n" ); - if (tops & OP_FLAGS) { - debug( pull ? " -> pulling flags\n" : " -> pushing flags\n" ); - sflags = smsg->flags; - aflags = sflags & ~*nflags; - dflags = ~sflags & *nflags; - unex = 0; - if (srec->status & S_EXPIRED) { - if (!pull) { - if (aflags || dflags) - info( "Info: Flags of expired message changed in (%d,%d)\n", srec->muid, srec->suid ); - return SYNC_OK; - } else { - if ((sflags & F_FLAGGED) && !(sflags & F_DELETED)) { - unex = 1; - dflags |= F_DELETED; - } else - return SYNC_OK; - } - } - rflags = (*nflags | aflags) & ~dflags; - if ((tops & OP_EXPUNGE) && (rflags & F_DELETED) && - (!tctx->conf->trash || tctx->conf->trash_only_new)) - { - aflags &= F_DELETED; - dflags = 0; - } - switch ((aflags | dflags) ? tdriver->set_flags( tctx, tmsg, tuid, aflags, dflags ) : DRV_OK) { - case DRV_STORE_BAD: return pull ? SYNC_SLAVE_BAD : SYNC_MASTER_BAD; - case DRV_BOX_BAD: return SYNC_FAIL; - default: /* ok */ break; - case DRV_OK: - *nflags = rflags; - if (unex) { - debug( "unexpiring pair(%d,%d)\n", srec->muid, srec->suid ); - /* log last, so deletion can't be misinterpreted! */ - Fprintf( jfp, "~ %d %d 0\n", srec->muid, srec->suid ); - srec->status &= ~S_EXPIRED; - } - } - } - } /* else b.4) / c.4) */ - return SYNC_OK; -} - -static int -sync_new( int tops, store_t *sctx, store_t *tctx, store_conf_t *tconf, FILE *jfp, sync_rec_t ***srecadd, int pull, int *smaxuid ) -{ - driver_t *tdriver = tctx->conf->driver, *sdriver = sctx->conf->driver; - sync_rec_t *srec; - message_t *msg; - int nmsgs, uid; - msg_data_t msgdata; - - for (nmsgs = 0, msg = sctx->msgs; msg; msg = msg->next) - if (!(msg->status & M_PROCESSED)) { - if (tops & OP_NEW) { - debug( pull ? "new message %d on master\n" : "new message %d on slave\n", msg->uid ); - if ((tops & OP_EXPUNGE) && (msg->flags & F_DELETED)) { - debug( pull ? " not pulling - would be expunged anyway\n" : " not pushing - would be expunged anyway\n" ); - msg->status |= M_NOT_SYNCED; - } else { - if ((msg->flags & F_FLAGGED) || !tconf->max_size || msg->size <= tconf->max_size) { - debug( pull ? " pulling it\n" : " pushing it\n" ); - if (!nmsgs) - info( pull ? "Pulling new messages..." : "Pushing new messages..." ); - else - infoc( '.' ); - nmsgs++; - msgdata.flags = msg->flags; - switch (sdriver->fetch_msg( sctx, msg, &msgdata )) { - case DRV_STORE_BAD: return pull ? SYNC_MASTER_BAD : SYNC_SLAVE_BAD; - case DRV_BOX_BAD: return SYNC_FAIL; - case DRV_MSG_BAD: /* ok */ msg->status |= M_NOT_SYNCED; continue; - } - msg->flags = msgdata.flags; - switch (tdriver->store_msg( tctx, &msgdata, &uid )) { - case DRV_STORE_BAD: return pull ? SYNC_SLAVE_BAD : SYNC_MASTER_BAD; - default: return SYNC_FAIL; - case DRV_OK: break; - } - } else { - debug( pull ? " not pulling - too big\n" : " not pushing - too big\n" ); - msg->status |= M_NOT_SYNCED; - uid = -1; - } - srec = nfmalloc( sizeof(*srec) ); - if (pull) { - srec->muid = msg->uid; - srec->suid = uid; - } else { - srec->muid = uid; - srec->suid = msg->uid; - } - srec->flags = msg->flags; - srec->status = 0; - srec->next = 0; - **srecadd = srec; - *srecadd = &srec->next; - Fprintf( jfp, "+ %d %d %u\n", srec->muid, srec->suid, srec->flags ); - if (*smaxuid < msg->uid) { - *smaxuid = msg->uid; - Fprintf( jfp, pull ? "( %d\n" : ") %d\n", msg->uid ); - } - } - } else - msg->status |= M_NOT_SYNCED; - } - if (nmsgs) - info( " %d messages\n", nmsgs ); - return SYNC_OK; -} static char * clean_strdup( const char *s ) @@ -450,21 +174,20 @@ clean_strdup( const char *s ) } int -sync_boxes( store_t *mctx, const char *mname, - store_t *sctx, const char *sname, - channel_conf_t *chan ) +sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan ) { - driver_t *mdriver = mctx->conf->driver, *sdriver = sctx->conf->driver; - message_t *mmsg, *smsg, *nmmsg, *nsmsg; + driver_t *driver[2]; + message_t *msg[2], *tmsg, *nmmsg, *nsmsg; sync_rec_t *recs, *srec, **srecadd, *nsrec; char *dname, *jname, *nname, *lname, *s, *cmname, *csname; FILE *dfp, *jfp, *nfp; - int mopts, sopts; - int nom, nos, delm, dels, mex, sex; - int muidval, suidval, smaxxuid, mmaxuid, smaxuid, minwuid, maxwuid; - int t1, t2, t3; - int lfd, ret, line, todel, delt, i, *mexcs, nmexcs, rmexcs; + int opts[2]; + int nom, nos, del[2], ex[M]; + int muidval, suidval, smaxxuid, maxuid[2], minwuid, maxwuid; + int t1, t2, t3, t; + int lfd, ret, line, todel, delt, *mexcs, nmexcs, rmexcs; unsigned char nflags; + msg_data_t msgdata; struct stat st; struct flock lck; char fbuf[16]; /* enlarge when support for keywords is added */ @@ -475,35 +198,63 @@ sync_boxes( store_t *mctx, const char *mname, nmmsg = nsmsg = 0; - mctx->uidvalidity = sctx->uidvalidity = 0; - mopts = sopts = 0; - makeopts( chan->sops, chan->slave, &sopts, chan->master, &mopts ); - makeopts( chan->mops, chan->master, &mopts, chan->slave, &sopts ); - if ((chan->sops & (OP_NEW|OP_RENEW)) && chan->max_messages) - sopts |= OPEN_OLD|OPEN_NEW|OPEN_FLAGS; - if (!mname || (mctx->conf->map_inbox && !strcmp( mctx->conf->map_inbox, mname ))) - mname = "INBOX"; - mctx->name = mname; - mdriver->prepare( mctx, mopts ); - if (!sname || (sctx->conf->map_inbox && !strcmp( sctx->conf->map_inbox, sname ))) - sname = "INBOX"; - sctx->name = sname; - sdriver->prepare( sctx, sopts ); + opts[M] = opts[S] = 0; + for (t = 0; t < 2; t++) { + if (chan->ops[t] & (OP_DELETE|OP_FLAGS)) { + opts[t] |= OPEN_SETFLAGS; + opts[1-t] |= OPEN_OLD; + if (chan->ops[t] & OP_FLAGS) + opts[1-t] |= OPEN_FLAGS; + } + if (chan->ops[t] & (OP_NEW|OP_RENEW)) { + opts[t] |= OPEN_APPEND; + if (chan->ops[t] & OP_RENEW) + opts[1-t] |= OPEN_OLD; + if (chan->ops[t] & OP_NEW) + opts[1-t] |= OPEN_NEW; + if (chan->ops[t] & OP_EXPUNGE) + opts[1-t] |= OPEN_FLAGS; + if (chan->stores[t]->max_size) + opts[1-t] |= OPEN_SIZE; + } + if (chan->ops[t] & OP_EXPUNGE) { + opts[t] |= OPEN_EXPUNGE; + if (chan->stores[t]->trash) { + if (!chan->stores[t]->trash_only_new) + opts[t] |= OPEN_OLD; + opts[t] |= OPEN_NEW|OPEN_FLAGS; + } else if (chan->stores[1-t]->trash && chan->stores[1-t]->trash_remote_new) + opts[t] |= OPEN_NEW|OPEN_FLAGS; + } + if (chan->ops[t] & OP_CREATE) + opts[t] |= OPEN_CREATE; + } + if ((chan->ops[S] & (OP_NEW|OP_RENEW)) && chan->max_messages) + opts[S] |= OPEN_OLD|OPEN_NEW|OPEN_FLAGS; + + for (t = 0; t < 2; t++) { + ctx[t]->name = + (!names[t] || (ctx[t]->conf->map_inbox && !strcmp( ctx[t]->conf->map_inbox, names[t] ))) ? + "INBOX" : names[t]; + ctx[t]->uidvalidity = 0; + driver[t] = ctx[t]->conf->driver; + driver[t]->prepare( ctx[t], opts[t] ); + } if (!strcmp( chan->sync_state ? chan->sync_state : global_sync_state, "*" )) { - if (!sctx->path) { - fprintf( stderr, "Error: store '%s' does not support in-box sync state\n", chan->slave->name ); - return SYNC_SLAVE_BAD; + if (!ctx[S]->path) { + fprintf( stderr, "Error: store '%s' does not support in-box sync state\n", chan->stores[S]->name ); + return SYNC_BAD(S); } - nfasprintf( &dname, "%s/." EXE "state", sctx->path ); + nfasprintf( &dname, "%s/." EXE "state", ctx[S]->path ); } else { - csname = clean_strdup( sname ); + csname = clean_strdup( ctx[S]->name ); if (chan->sync_state) nfasprintf( &dname, "%s%s", chan->sync_state, csname ); else { - cmname = clean_strdup( mname ); + cmname = clean_strdup( ctx[M]->name ); nfasprintf( &dname, "%s:%s:%s_:%s:%s", global_sync_state, - chan->master->name, cmname, chan->slave->name, csname ); + chan->stores[M]->name, cmname, chan->stores[S]->name, csname ); free( cmname ); } free( csname ); @@ -511,7 +262,7 @@ sync_boxes( store_t *mctx, const char *mname, nfasprintf( &jname, "%s.journal", dname ); nfasprintf( &nname, "%s.new", dname ); nfasprintf( &lname, "%s.lock", dname ); - muidval = suidval = smaxxuid = mmaxuid = smaxuid = 0; + muidval = suidval = smaxxuid = maxuid[M] = maxuid[S] = 0; memset( &lck, 0, sizeof(lck) ); #if SEEK_SET != 0 lck.l_whence = SEEK_SET; @@ -531,19 +282,19 @@ sync_boxes( store_t *mctx, const char *mname, if (fcntl( lfd, F_SETLK, &lck )) { lckerr: fprintf( stderr, "Error: channel :%s:%s-:%s:%s is locked\n", - chan->master->name, mname, chan->slave->name, sname ); + chan->stores[M]->name, ctx[M]->name, chan->stores[S]->name, ctx[S]->name ); ret = SYNC_FAIL; goto bail1; } if ((dfp = fopen( dname, "r" ))) { debug( "reading sync state %s ...\n", dname ); - if (!fgets( buf, sizeof(buf), dfp ) || !(i = strlen( buf )) || buf[i - 1] != '\n') { + if (!fgets( buf, sizeof(buf), dfp ) || !(t = strlen( buf )) || buf[t - 1] != '\n') { fprintf( stderr, "Error: incomplete sync state header in %s\n", dname ); fclose( dfp ); ret = SYNC_FAIL; goto bail; } - if (sscanf( buf, "%d:%d %d:%d:%d", &muidval, &mmaxuid, &suidval, &smaxxuid, &smaxuid) != 5) { + if (sscanf( buf, "%d:%d %d:%d:%d", &muidval, &maxuid[M], &suidval, &smaxxuid, &maxuid[S]) != 5) { fprintf( stderr, "Error: invalid sync state header in %s\n", dname ); fclose( dfp ); ret = SYNC_FAIL; @@ -552,7 +303,7 @@ sync_boxes( store_t *mctx, const char *mname, line = 1; while (fgets( buf, sizeof(buf), dfp )) { line++; - if (!(i = strlen( buf )) || buf[i - 1] != '\n') { + if (!(t = strlen( buf )) || buf[t - 1] != '\n') { fprintf( stderr, "Error: incomplete sync state entry at %s:%d\n", dname, line ); fclose( dfp ); ret = SYNC_FAIL; @@ -566,8 +317,8 @@ sync_boxes( store_t *mctx, const char *mname, goto bail; } srec = nfmalloc( sizeof(*srec) ); - srec->muid = t1; - srec->suid = t2; + srec->uid[M] = t1; + srec->uid[S] = t2; s = fbuf; if (*s == 'X') { s++; @@ -575,7 +326,7 @@ sync_boxes( store_t *mctx, const char *mname, } else srec->status = 0; srec->flags = parse_flags( s ); - debug( " entry (%d,%d,%u,%s)\n", srec->muid, srec->suid, srec->flags, srec->status & S_EXPIRED ? "X" : "" ); + debug( " entry (%d,%d,%u,%s)\n", srec->uid[M], srec->uid[S], srec->flags, srec->status & S_EXPIRED ? "X" : "" ); srec->next = 0; *srecadd = srec; srecadd = &srec->next; @@ -595,7 +346,7 @@ sync_boxes( store_t *mctx, const char *mname, srec = recs; while (fgets( buf, sizeof(buf), jfp )) { line++; - if (!(i = strlen( buf )) || buf[i - 1] != '\n') { + if (!(t = strlen( buf )) || buf[t - 1] != '\n') { fprintf( stderr, "Error: incomplete journal entry at %s:%d\n", jname, line ); fclose( jfp ); ret = SYNC_FAIL; @@ -616,16 +367,16 @@ sync_boxes( store_t *mctx, const char *mname, goto bail; } if (buf[0] == '(') - mmaxuid = t1; + maxuid[M] = t1; else if (buf[0] == ')') - smaxuid = t1; + maxuid[S] = t1; else if (buf[0] == '|') { muidval = t1; suidval = t2; } else if (buf[0] == '+') { srec = nfmalloc( sizeof(*srec) ); - srec->muid = t1; - srec->suid = t2; + srec->uid[M] = t1; + srec->uid[S] = t2; srec->flags = t3; debug( " new entry(%d,%d,%u)\n", t1, t2, t3 ); srec->status = 0; @@ -634,14 +385,14 @@ sync_boxes( store_t *mctx, const char *mname, srecadd = &srec->next; } else { for (; srec; srec = srec->next) - if (srec->muid == t1 && srec->suid == t2) + if (srec->uid[M] == t1 && srec->uid[S] == t2) goto syncfnd; fprintf( stderr, "Error: journal entry at %s:%d refers to non-existing sync state entry\n", jname, line ); fclose( jfp ); ret = SYNC_FAIL; goto bail; syncfnd: - debug( " entry(%d,%d,%u) ", srec->muid, srec->suid, srec->flags ); + debug( " entry(%d,%d,%u) ", srec->uid[M], srec->uid[S], srec->flags ); switch (buf[0]) { case '-': debug( "killed\n" ); @@ -649,11 +400,11 @@ sync_boxes( store_t *mctx, const char *mname, break; case '<': debug( "master now %d\n", t3 ); - srec->muid = t3; + srec->uid[M] = t3; break; case '>': debug( "slave now %d\n", t3 ); - srec->suid = t3; + srec->uid[S] = t3; break; case '*': debug( "flags now %d\n", t3 ); @@ -688,25 +439,25 @@ sync_boxes( store_t *mctx, const char *mname, } skiprd: - if (sctx->opts & OPEN_NEW) + if (ctx[S]->opts & OPEN_NEW) maxwuid = INT_MAX; - else if (sctx->opts & OPEN_OLD) { + else if (ctx[S]->opts & OPEN_OLD) { maxwuid = 0; for (srec = recs; srec; srec = srec->next) - if (!(srec->status & S_DEAD) && srec->suid > maxwuid) - maxwuid = srec->suid; + if (!(srec->status & S_DEAD) && srec->uid[S] > maxwuid) + maxwuid = srec->uid[S]; } else maxwuid = 0; - info( "Selecting slave %s... ", sname ); + info( "Selecting slave %s... ", ctx[S]->name ); debug( "selecting slave [1,%d]\n", maxwuid ); - switch (sdriver->select( sctx, (sctx->opts & OPEN_OLD) ? 1 : smaxuid + 1, maxwuid, 0, 0 )) { - case DRV_STORE_BAD: ret = SYNC_SLAVE_BAD; goto bail; + switch (driver[S]->select( ctx[S], (ctx[S]->opts & OPEN_OLD) ? 1 : maxuid[S] + 1, maxwuid, 0, 0 )) { + case DRV_STORE_BAD: ret = SYNC_BAD(S); goto bail; case DRV_BOX_BAD: ret = SYNC_FAIL; goto bail; } - info( "%d messages, %d recent\n", sctx->count, sctx->recent ); - dump_box( sctx ); + info( "%d messages, %d recent\n", ctx[S]->count, ctx[S]->recent ); + dump_box( ctx[S] ); - if (suidval && suidval != sctx->uidvalidity) { + if (suidval && suidval != ctx[S]->uidvalidity) { fprintf( stderr, "Error: UIDVALIDITY of slave changed\n" ); ret = SYNC_FAIL; goto bail; @@ -744,139 +495,293 @@ sync_boxes( store_t *mctx, const char *mname, if (srec->status & S_DEAD) continue; if (srec->status & S_EXPIRED) { - if (!srec->suid || ((sctx->opts & OPEN_OLD) && !findmsg( sctx, srec->suid, &nsmsg, "slave" ))) - srec->status |= S_EXP_SLAVE; - else if (minwuid > srec->muid) - minwuid = srec->muid; - } else if (smaxxuid < srec->suid && minwuid > srec->muid) - minwuid = srec->muid; + if (!srec->uid[S] || ((ctx[S]->opts & OPEN_OLD) && !findmsg( ctx[S], srec->uid[S], &nsmsg, "slave" ))) + srec->status |= S_EXP_S; + else if (minwuid > srec->uid[M]) + minwuid = srec->uid[M]; + } else if (smaxxuid < srec->uid[S] && minwuid > srec->uid[M]) + minwuid = srec->uid[M]; } debug( " min non-orphaned master uid is %d\n", minwuid ); - Fprintf( jfp, "^\n" ); /* if any S_EXP_SLAVE */ + Fprintf( jfp, "^\n" ); /* if any S_EXP_S */ for (srec = recs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; - if (srec->status & S_EXP_SLAVE) { - if (minwuid > srec->muid && mmaxuid >= srec->muid) { - debug( " -> killing (%d,%d)\n", srec->muid, srec->suid ); + if (srec->status & S_EXP_S) { + if (minwuid > srec->uid[M] && maxuid[M] >= srec->uid[M]) { + debug( " -> killing (%d,%d)\n", srec->uid[M], srec->uid[S] ); srec->status = S_DEAD; - Fprintf( jfp, "- %d %d\n", srec->muid, srec->suid ); - } else if (srec->suid) { - debug( " -> orphaning (%d,[%d])\n", srec->muid, srec->suid ); - Fprintf( jfp, "> %d %d 0\n", srec->muid, srec->suid ); - srec->suid = 0; + Fprintf( jfp, "- %d %d\n", srec->uid[M], srec->uid[S] ); + } else if (srec->uid[S]) { + debug( " -> orphaning (%d,[%d])\n", srec->uid[M], srec->uid[S] ); + Fprintf( jfp, "> %d %d 0\n", srec->uid[M], srec->uid[S] ); + srec->uid[S] = 0; } - } else if (minwuid > srec->muid) { - if (srec->suid < 0) { - if (mmaxuid >= srec->muid) { - debug( " -> killing (%d,%d)\n", srec->muid, srec->suid ); + } else if (minwuid > srec->uid[M]) { + if (srec->uid[S] < 0) { + if (maxuid[M] >= srec->uid[M]) { + debug( " -> killing (%d,%d)\n", srec->uid[M], srec->uid[S] ); srec->status = S_DEAD; - Fprintf( jfp, "- %d %d\n", srec->muid, srec->suid ); + Fprintf( jfp, "- %d %d\n", srec->uid[M], srec->uid[S] ); } - } else if (srec->muid > 0 && srec->suid && (mctx->opts & OPEN_OLD) && - (!(mctx->opts & OPEN_NEW) || mmaxuid >= srec->muid)) { + } else if (srec->uid[M] > 0 && srec->uid[S] && (ctx[M]->opts & OPEN_OLD) && + (!(ctx[M]->opts & OPEN_NEW) || maxuid[M] >= srec->uid[M])) { if (nmexcs == rmexcs) { rmexcs = rmexcs * 2 + 100; mexcs = nfrealloc( mexcs, rmexcs * sizeof(int) ); } - mexcs[nmexcs++] = srec->muid; + mexcs[nmexcs++] = srec->uid[M]; } } } debug( " exception list is:" ); - for (i = 0; i < nmexcs; i++) - debug( " %d", mexcs[i] ); + for (t = 0; t < nmexcs; t++) + debug( " %d", mexcs[t] ); debug( "\n" ); - } else if (mctx->opts & OPEN_OLD) + } else if (ctx[M]->opts & OPEN_OLD) minwuid = 1; - if (mctx->opts & OPEN_NEW) { - if (minwuid > mmaxuid + 1) - minwuid = mmaxuid + 1; + if (ctx[M]->opts & OPEN_NEW) { + if (minwuid > maxuid[M] + 1) + minwuid = maxuid[M] + 1; maxwuid = INT_MAX; - } else if (mctx->opts & OPEN_OLD) { + } else if (ctx[M]->opts & OPEN_OLD) { maxwuid = 0; for (srec = recs; srec; srec = srec->next) - if (!(srec->status & S_DEAD) && srec->muid > maxwuid) - maxwuid = srec->muid; + if (!(srec->status & S_DEAD) && srec->uid[M] > maxwuid) + maxwuid = srec->uid[M]; } else maxwuid = 0; - info( "Selecting master %s... ", mname ); + info( "Selecting master %s... ", ctx[M]->name ); debug( "selecting master [%d,%d]\n", minwuid, maxwuid ); - switch (mdriver->select( mctx, minwuid, maxwuid, mexcs, nmexcs )) { - case DRV_STORE_BAD: ret = SYNC_MASTER_BAD; goto finish; + switch (driver[M]->select( ctx[M], minwuid, maxwuid, mexcs, nmexcs )) { + case DRV_STORE_BAD: ret = SYNC_BAD(M); goto finish; case DRV_BOX_BAD: ret = SYNC_FAIL; goto finish; } - info( "%d messages, %d recent\n", mctx->count, mctx->recent ); - dump_box( mctx ); + info( "%d messages, %d recent\n", ctx[M]->count, ctx[M]->recent ); + dump_box( ctx[M] ); - if (muidval && muidval != mctx->uidvalidity) { + if (muidval && muidval != ctx[M]->uidvalidity) { fprintf( stderr, "Error: UIDVALIDITY of master changed\n" ); ret = SYNC_FAIL; goto finish; } if (!muidval || !suidval) { - muidval = mctx->uidvalidity; - suidval = sctx->uidvalidity; + muidval = ctx[M]->uidvalidity; + suidval = ctx[S]->uidvalidity; Fprintf( jfp, "| %d %d\n", muidval, suidval ); } - info( "Synchronizing\n" ); + info( "Synchronizing...\n" ); debug( "synchronizing old entries\n" ); Fprintf( jfp, "^\n" ); for (srec = recs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; - debug( "pair (%d,%d)\n", srec->muid, srec->suid ); - mmsg = findmsg( mctx, srec->muid, &nmmsg, "master" ); - smsg = (srec->status & S_EXP_SLAVE) ? 0 : findmsg( sctx, srec->suid, &nsmsg, "slave" ); - nom = !mmsg && (mctx->opts & OPEN_OLD); - nos = !smsg && (sctx->opts & OPEN_OLD); + debug( "pair (%d,%d)\n", srec->uid[M], srec->uid[S] ); + msg[M] = findmsg( ctx[M], srec->uid[M], &nmmsg, "master" ); + msg[S] = (srec->status & S_EXP_S) ? 0 : findmsg( ctx[S], srec->uid[S], &nsmsg, "slave" ); + nom = !msg[M] && (ctx[M]->opts & OPEN_OLD); + nos = !msg[S] && (ctx[S]->opts & OPEN_OLD); if (nom && nos) { debug( " vanished\n" ); /* d.1) d.5) d.6) d.10) d.11) */ srec->status = S_DEAD; - Fprintf( jfp, "- %d %d\n", srec->muid, srec->suid ); + Fprintf( jfp, "- %d %d\n", srec->uid[M], srec->uid[S] ); } else { - delm = nom && (srec->muid > 0); - dels = nos && (srec->suid > 0); + del[M] = nom && (srec->uid[M] > 0); + del[S] = nos && (srec->uid[S] > 0); nflags = srec->flags; - if ((ret = sync_old( chan->mops, sctx, mctx, chan->master, jfp, 0, &nflags, srec, smsg, mmsg, dels, delm )) != SYNC_OK || - (ret = sync_old( chan->sops, mctx, sctx, chan->slave, jfp, 1, &nflags, srec, mmsg, smsg, delm, dels )) != SYNC_OK) - goto finish; + for (t = 0; t < 2; t++) { + int uid, unex; + unsigned char sflags, aflags, dflags, rflags; + + /* excludes (push) c.3) d.2) d.3) d.4) / (pull) b.3) d.7) d.8) d.9) */ + if (!srec->uid[t]) { + /* b.1) / c.1) */ + debug( " no more %s\n", str_ms[t] ); + } else if (del[1-t]) { + /* c.4) d.9) / b.4) d.4) */ + debug( " %s vanished\n", str_ms[1-t] ); + if (msg[t] && msg[t]->flags != nflags) + info( "Info: conflicting changes in (%d,%d)\n", srec->uid[M], srec->uid[S] ); + if (chan->ops[t] & OP_DELETE) { + debug( " -> %s delete\n", str_hl[t] ); + switch (driver[t]->set_flags( ctx[t], msg[t], srec->uid[t], F_DELETED, 0 )) { + case DRV_STORE_BAD: ret = SYNC_BAD(t); goto finish; + case DRV_BOX_BAD: ret = SYNC_FAIL; goto finish; + default: /* ok */ break; + case DRV_OK: + Fprintf( jfp, "%c %d %d 0\n", "><"[t], srec->uid[M], srec->uid[S] ); + srec->uid[1-t] = 0; + } + } + } else if (!msg[1-t]) + /* c.1) c.2) d.7) d.8) / b.1) b.2) d.2) d.3) */ + ; + else if (srec->uid[t] < 0) { + /* b.2) / c.2) */ + debug( " no %s yet\n", str_ms[t] ); + if (chan->ops[t] & OP_RENEW) { + if ((chan->ops[t] & OP_EXPUNGE) && (msg[1-t]->flags & F_DELETED)) { + debug( " -> not %sing - would be expunged anyway\n", str_hl[t] ); + msg[1-t]->status |= M_NOT_SYNCED; + } else { + if ((msg[1-t]->flags & F_FLAGGED) || !chan->stores[t]->max_size || msg[1-t]->size <= chan->stores[t]->max_size) { + debug( " -> %sing it\n", str_hl[t] ); + msgdata.flags = msg[1-t]->flags; + switch (driver[1-t]->fetch_msg( ctx[1-t], msg[1-t], &msgdata )) { + case DRV_STORE_BAD: ret = SYNC_BAD(1-t); goto finish; + case DRV_BOX_BAD: ret = SYNC_FAIL; goto finish; + default: /* ok */ msg[1-t]->status |= M_NOT_SYNCED; break; + case DRV_OK: + msg[1-t]->flags = msgdata.flags; + switch (driver[t]->store_msg( ctx[t], &msgdata, &uid )) { + case DRV_STORE_BAD: ret = SYNC_BAD(t); goto finish; + default: ret = SYNC_FAIL; goto finish; + case DRV_OK: + Fprintf( jfp, "%c %d %d %d\n", "<>"[t], srec->uid[M], srec->uid[S], uid ); + srec->uid[t] = uid; + nflags = msg[1-t]->flags; + } + } + } else { + debug( " -> not %sing - still too big\n", str_hl[t] ); + msg[1-t]->status |= M_NOT_SYNCED; + } + } + } else + msg[1-t]->status |= M_NOT_SYNCED; + } else if (!del[t]) { + /* a) & b.3) / c.3) */ + debug( " may %s\n", str_hl[t] ); + if (chan->ops[t] & OP_FLAGS) { + debug( " -> %sing flags\n", str_hl[t] ); + sflags = msg[1-t]->flags; + aflags = sflags & ~nflags; + dflags = ~sflags & nflags; + unex = 0; + if (srec->status & S_EXPIRED) { + if (!t) { + if (aflags || dflags) + info( "Info: Flags of expired message changed in (%d,%d)\n", srec->uid[M], srec->uid[S] ); + continue; + } else { + if ((sflags & F_FLAGGED) && !(sflags & F_DELETED)) { + unex = 1; + dflags |= F_DELETED; + } else + continue; + } + } + rflags = (nflags | aflags) & ~dflags; + if ((chan->ops[t] & OP_EXPUNGE) && (rflags & F_DELETED) && + (!ctx[t]->conf->trash || ctx[t]->conf->trash_only_new)) + { + aflags &= F_DELETED; + dflags = 0; + } + switch ((aflags | dflags) ? driver[t]->set_flags( ctx[t], msg[t], srec->uid[t], aflags, dflags ) : DRV_OK) { + case DRV_STORE_BAD: ret = SYNC_BAD(t); goto finish; + case DRV_BOX_BAD: ret = SYNC_FAIL; goto finish; + default: /* ok */ break; + case DRV_OK: + nflags = rflags; + if (unex) { + debug( "unexpiring pair(%d,%d)\n", srec->uid[M], srec->uid[S] ); + /* log last, so deletion can't be misinterpreted! */ + Fprintf( jfp, "~ %d %d 0\n", srec->uid[M], srec->uid[S] ); + srec->status &= ~S_EXPIRED; + } + } + } + } /* else b.4) / c.4) */ + } if (srec->flags != nflags) { debug( " updating flags (%u -> %u)\n", srec->flags, nflags ); srec->flags = nflags; - Fprintf( jfp, "* %d %d %u\n", srec->muid, srec->suid, nflags ); + Fprintf( jfp, "* %d %d %u\n", srec->uid[M], srec->uid[S], nflags ); } - if (mmsg && (mmsg->flags & F_DELETED)) - srec->status |= S_DEL_MASTER; - if (smsg && (smsg->flags & F_DELETED)) - srec->status |= S_DEL_SLAVE; + if (msg[M] && (msg[M]->flags & F_DELETED)) + srec->status |= S_DEL(M); + if (msg[S] && (msg[S]->flags & F_DELETED)) + srec->status |= S_DEL(S); } } debug( "synchronizing new entries\n" ); - if ((ret = sync_new( chan->mops, sctx, mctx, chan->master, jfp, &srecadd, 0, &smaxuid )) != SYNC_OK || - (ret = sync_new( chan->sops, mctx, sctx, chan->slave, jfp, &srecadd, 1, &mmaxuid )) != SYNC_OK) - goto finish; + for (t = 0; t < 2; t++) { + int nmsgs, uid; - if ((chan->sops & (OP_NEW|OP_RENEW)) && chan->max_messages) { + for (nmsgs = 0, tmsg = ctx[1-t]->msgs; tmsg; tmsg = tmsg->next) + if (!(tmsg->status & M_PROCESSED)) { + if (chan->ops[t] & OP_NEW) { + debug( "new message %d on %s\n", tmsg->uid, str_ms[1-t] ); + if ((chan->ops[t] & OP_EXPUNGE) && (tmsg->flags & F_DELETED)) { + debug( " not %sing - would be expunged anyway\n", str_hl[t] ); + tmsg->status |= M_NOT_SYNCED; + } else { + if ((tmsg->flags & F_FLAGGED) || !chan->stores[t]->max_size || tmsg->size <= chan->stores[t]->max_size) { + debug( " %sing it\n", str_hl[t] ); + if (!nmsgs) + info( t ? "Pulling new messages..." : "Pushing new messages..." ); + else + infoc( '.' ); + nmsgs++; + msgdata.flags = tmsg->flags; + switch (driver[1-t]->fetch_msg( ctx[1-t], tmsg, &msgdata )) { + case DRV_STORE_BAD: return SYNC_BAD(1-t); + case DRV_BOX_BAD: return SYNC_FAIL; + case DRV_MSG_BAD: /* ok */ tmsg->status |= M_NOT_SYNCED; continue; + } + tmsg->flags = msgdata.flags; + switch (driver[t]->store_msg( ctx[t], &msgdata, &uid )) { + case DRV_STORE_BAD: return SYNC_BAD(t); + default: return SYNC_FAIL; + case DRV_OK: break; + } + } else { + debug( " not %sing - too big\n", str_hl[t] ); + tmsg->status |= M_NOT_SYNCED; + uid = -1; + } + srec = nfmalloc( sizeof(*srec) ); + srec->uid[1-t] = tmsg->uid; + srec->uid[t] = uid; + srec->flags = tmsg->flags; + srec->status = 0; + srec->next = 0; + *srecadd = srec; + srecadd = &srec->next; + Fprintf( jfp, "+ %d %d %u\n", srec->uid[M], srec->uid[S], srec->flags ); + if (maxuid[1-t] < tmsg->uid) { + maxuid[1-t] = tmsg->uid; + Fprintf( jfp, "%c %d\n", ")("[t], tmsg->uid ); + } + } + } else + tmsg->status |= M_NOT_SYNCED; + } + if (nmsgs) + info( " %d messages\n", nmsgs ); + } + + if ((chan->ops[S] & (OP_NEW|OP_RENEW)) && chan->max_messages) { debug( "expiring excess entries\n" ); - todel = sctx->count - chan->max_messages; - for (smsg = sctx->msgs; smsg && todel > 0; smsg = smsg->next) - if (!(smsg->status & M_DEAD) && (smsg->flags & F_DELETED)) + todel = ctx[S]->count - chan->max_messages; + for (tmsg = ctx[S]->msgs; tmsg && todel > 0; tmsg = tmsg->next) + if (!(tmsg->status & M_DEAD) && (tmsg->flags & F_DELETED)) todel--; delt = 0; - for (smsg = sctx->msgs; smsg && todel > 0; smsg = smsg->next) { - if ((smsg->status & M_DEAD) || (smsg->flags & F_DELETED)) + for (tmsg = ctx[S]->msgs; tmsg && todel > 0; tmsg = tmsg->next) { + if ((tmsg->status & M_DEAD) || (tmsg->flags & F_DELETED)) continue; - if ((smsg->flags & F_FLAGGED) || (smsg->status & M_NOT_SYNCED)) /* add M_DESYNCED? */ + if ((tmsg->flags & F_FLAGGED) || (tmsg->status & M_NOT_SYNCED)) /* add M_DESYNCED? */ todel--; - else if (!(smsg->status & M_RECENT)) { - smsg->status |= M_EXPIRED; + else if (!(tmsg->status & M_RECENT)) { + tmsg->status |= M_EXPIRED; delt++; todel--; } @@ -886,19 +791,19 @@ sync_boxes( store_t *mctx, const char *mname, for (srec = recs; srec; srec = srec->next) { if (srec->status & (S_DEAD|S_EXPIRED)) continue; - smsg = findmsg( sctx, srec->suid, &nsmsg, "slave" ); - if (smsg && (smsg->status & M_EXPIRED)) { - debug( " expiring pair(%d,%d)\n", srec->muid, srec->suid ); + tmsg = findmsg( ctx[S], srec->uid[S], &nsmsg, "slave" ); + if (tmsg && (tmsg->status & M_EXPIRED)) { + debug( " expiring pair(%d,%d)\n", srec->uid[M], srec->uid[S] ); /* log first, so deletion can't be misinterpreted! */ - Fprintf( jfp, "~ %d %d 1\n", srec->muid, srec->suid ); - if (smaxxuid < srec->suid) - smaxxuid = srec->suid; + Fprintf( jfp, "~ %d %d 1\n", srec->uid[M], srec->uid[S] ); + if (smaxxuid < srec->uid[S]) + smaxxuid = srec->uid[S]; srec->status |= S_EXPIRED; - switch (sdriver->set_flags( sctx, smsg, 0, F_DELETED, 0 )) { - case DRV_STORE_BAD: ret = SYNC_SLAVE_BAD; goto finish; + switch (driver[S]->set_flags( ctx[S], tmsg, 0, F_DELETED, 0 )) { + case DRV_STORE_BAD: ret = SYNC_BAD(S); goto finish; case DRV_BOX_BAD: ret = SYNC_FAIL; goto finish; default: /* ok */ break; - case DRV_OK: srec->status |= S_DEL_SLAVE; + case DRV_OK: srec->status |= S_DEL(S); } } } @@ -908,28 +813,54 @@ sync_boxes( store_t *mctx, const char *mname, /* Doing CLOSE here instead of EXPUNGE above saves network traffic. But it costs more server power for single-file formats. And it makes disk-full/quota-exceeded more probable. */ - mex = sex = 0; - if (chan->mops & OP_EXPUNGE) { - info( "Expunging master\n" ); - debug( "expunging master\n" ); - switch (expunge( mctx, sctx )) { - case EX_STORE_BAD: ret = SYNC_MASTER_BAD; goto finish; - case EX_RSTORE_BAD: ret = SYNC_SLAVE_BAD; goto finish; - default: ret = SYNC_FAIL; break; - case EX_OK: mex = 1; + for (t = 0; t < 2; t++) { + ex[t] = 0; + if (chan->ops[t] & OP_EXPUNGE) { + info( "Expunging %s\n", str_ms[t] ); + debug( "expunging %s\n", str_ms[t] ); + for (tmsg = ctx[t]->msgs; tmsg; tmsg = tmsg->next) + if (tmsg->flags & F_DELETED) { + if (ctx[t]->conf->trash) { + if (!ctx[t]->conf->trash_only_new || (tmsg->status & M_NOT_SYNCED)) { + debug( " trashing message %d\n", tmsg->uid ); + switch (driver[t]->trash_msg( ctx[t], tmsg )) { + case DRV_OK: break; + case DRV_STORE_BAD: ret = SYNC_BAD(t); goto finish; + default: ret = SYNC_FAIL; goto nexex; + } + } else + debug( " not trashing message %d - not new\n", tmsg->uid ); + } else if (ctx[1-t]->conf->trash && ctx[1-t]->conf->trash_remote_new) { + if (tmsg->status & M_NOT_SYNCED) { + if (!ctx[1-t]->conf->max_size || tmsg->size <= ctx[1-t]->conf->max_size) { + debug( " remote trashing message %d\n", tmsg->uid ); + msgdata.flags = tmsg->flags; + switch (driver[t]->fetch_msg( ctx[t], tmsg, &msgdata )) { + case DRV_OK: break; + case DRV_STORE_BAD: ret = SYNC_BAD(t); goto finish; + default: ret = SYNC_FAIL; goto nexex; + } + switch (driver[1-t]->store_msg( ctx[1-t], &msgdata, 0 )) { + case DRV_OK: break; + case DRV_STORE_BAD: ret = SYNC_BAD(1-t); goto finish; + default: ret = SYNC_FAIL; goto nexex; + } + } else + debug( " not remote trashing message %d - too big\n", tmsg->uid ); + } else + debug( " not remote trashing message %d - not new\n", tmsg->uid ); + } + } + + switch (driver[t]->close( ctx[t] )) { + case DRV_OK: ex[t] = 1; break; + case DRV_STORE_BAD: ret = SYNC_BAD(t); goto finish; + default: break; + } } + nexex: ; } - if (chan->sops & OP_EXPUNGE) { - info( "Expunging slave\n" ); - debug( "expunging slave\n" ); - switch (expunge( sctx, mctx )) { - case EX_STORE_BAD: ret = SYNC_SLAVE_BAD; goto finish; - case EX_RSTORE_BAD: ret = SYNC_MASTER_BAD; goto finish; - default: ret = SYNC_FAIL; break; - case EX_OK: sex = 1; - } - } - if (mex || sex) { + if (ex[M] || ex[S]) { /* This cleanup is not strictly necessary, as the next full sync would throw out the dead entries anyway. But ... */ @@ -939,10 +870,10 @@ sync_boxes( store_t *mctx, const char *mname, for (srec = recs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; - if (!((srec->suid <= 0 || ((srec->status & S_DEL_SLAVE) && sex)) && - (srec->muid <= 0 || ((srec->status & S_DEL_MASTER) && mex) || (srec->status & S_EXPIRED))) && - smaxxuid < srec->suid && minwuid > srec->muid) - minwuid = srec->muid; + if (!((srec->uid[S] <= 0 || ((srec->status & S_DEL(S)) && ex[S])) && + (srec->uid[M] <= 0 || ((srec->status & S_DEL(M)) && ex[M]) || (srec->status & S_EXPIRED))) && + smaxxuid < srec->uid[S] && minwuid > srec->uid[M]) + minwuid = srec->uid[M]; } debug( " min non-orphaned master uid is %d\n", minwuid ); } @@ -951,20 +882,20 @@ sync_boxes( store_t *mctx, const char *mname, for (srec = recs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; - if (srec->suid <= 0 || ((srec->status & S_DEL_SLAVE) && sex)) { - if (srec->muid <= 0 || ((srec->status & S_DEL_MASTER) && mex)) { - debug( " -> killing (%d,%d)\n", srec->muid, srec->suid ); + if (srec->uid[S] <= 0 || ((srec->status & S_DEL(S)) && ex[S])) { + if (srec->uid[M] <= 0 || ((srec->status & S_DEL(M)) && ex[M])) { + debug( " -> killing (%d,%d)\n", srec->uid[M], srec->uid[S] ); srec->status = S_DEAD; - Fprintf( jfp, "- %d %d\n", srec->muid, srec->suid ); + Fprintf( jfp, "- %d %d\n", srec->uid[M], srec->uid[S] ); } else if (srec->status & S_EXPIRED) { - if (mmaxuid >= srec->muid && minwuid > srec->muid) { - debug( " -> killing (%d,%d)\n", srec->muid, srec->suid ); + if (maxuid[M] >= srec->uid[M] && minwuid > srec->uid[M]) { + debug( " -> killing (%d,%d)\n", srec->uid[M], srec->uid[S] ); srec->status = S_DEAD; - Fprintf( jfp, "- %d %d\n", srec->muid, srec->suid ); - } else if (srec->suid) { - debug( " -> orphaning (%d,[%d])\n", srec->muid, srec->suid ); - Fprintf( jfp, "> %d %d 0\n", srec->muid, srec->suid ); - srec->suid = 0; + Fprintf( jfp, "- %d %d\n", srec->uid[M], srec->uid[S] ); + } else if (srec->uid[S]) { + debug( " -> orphaning (%d,[%d])\n", srec->uid[M], srec->uid[S] ); + Fprintf( jfp, "> %d %d 0\n", srec->uid[M], srec->uid[S] ); + srec->uid[S] = 0; } } } @@ -972,12 +903,12 @@ sync_boxes( store_t *mctx, const char *mname, } finish: - Fprintf( nfp, "%d:%d %d:%d:%d\n", muidval, mmaxuid, suidval, smaxxuid, smaxuid ); + Fprintf( nfp, "%d:%d %d:%d:%d\n", muidval, maxuid[M], suidval, smaxxuid, maxuid[S] ); for (srec = recs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; make_flags( srec->flags, fbuf ); - Fprintf( nfp, "%d %d %s%s\n", srec->muid, srec->suid, + Fprintf( nfp, "%d %d %s%s\n", srec->uid[M], srec->uid[S], srec->status & S_EXPIRED ? "X" : "", fbuf ); }