isync

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

commit e565d08246205e30f2cc7af169a52be14d765898
parent e8caaaaf6594cd25702d9f19744426828cd5a260
Author: Oswald Buddenhagen <ossi@users.sf.net>
Date:   Wed,  8 Jan 2020 18:22:48 +0100

don't try to propagate flags the target store does not support

$Forwarded is not standard, so it will most likely fail with mailboxes
that do not support keywords.

amends c4d7f018.

Diffstat:
Msrc/driver.h | 3+++
Msrc/drv_imap.c | 31+++++++++++++++++++++++++++++++
Msrc/drv_maildir.c | 7+++++++
Msrc/sync.c | 25+++++++++++++++++++++++--
4 files changed, 64 insertions(+), 2 deletions(-)

diff --git a/src/driver.h b/src/driver.h @@ -181,6 +181,9 @@ struct driver { /* Return the minimal UID the next stored message will have. */ int (*get_uidnext)( store_t *ctx ); + /* Return the flags that can be stored in the selected mailbox. */ + xint (*get_supported_flags)( store_t *ctx ); + /* Confirm that the open mailbox is empty. */ int (*confirm_box_empty)( store_t *ctx ); diff --git a/src/drv_imap.c b/src/drv_imap.c @@ -105,6 +105,7 @@ struct imap_store { /* trash folder's existence is not confirmed yet */ enum { TrashUnknown, TrashChecking, TrashKnown } trashnc; uint got_namespace:1; + uint has_forwarded:1; char delimiter[2]; /* hierarchy delimiter */ list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */ string_list_t *boxes; // _list results @@ -1199,6 +1200,27 @@ parse_response_code( imap_store_t *ctx, imap_cmd_t *cmd, char *s ) error( "IMAP error: malformed APPENDUID status\n" ); return RESP_CANCEL; } + } else if (!strcmp( "PERMANENTFLAGS", arg )) { + parse_list_init( &ctx->parse_list_sts ); + if (parse_imap_list( NULL, &s, &ctx->parse_list_sts ) != LIST_OK) { + error( "IMAP error: malformed PERMANENTFLAGS status\n" ); + return RESP_CANCEL; + } + int ret = RESP_OK; + for (list_t *tmp = ctx->parse_list_sts.head->child; tmp; tmp = tmp->next) { + if (!is_atom( tmp )) { + error( "IMAP error: malformed PERMANENTFLAGS status item\n" ); + ret = RESP_CANCEL; + break; + } + if (!strcmp( tmp->val, "\\*" ) || !strcmp( tmp->val, "$Forwarded" )) { + ctx->has_forwarded = 1; + break; + } + } + free_list( ctx->parse_list_sts.head ); + ctx->parse_list_sts.head = NULL; + return ret; } return RESP_OK; } @@ -2432,6 +2454,14 @@ imap_get_uidnext( store_t *gctx ) return ctx->uidnext; } +static xint +imap_get_supported_flags( store_t *gctx ) +{ + imap_store_t *ctx = (imap_store_t *)gctx; + + return ctx->has_forwarded ? 255 : (255 & ~F_FORWARDED); +} + /******************* imap_create_box *******************/ static void @@ -3433,6 +3463,7 @@ struct driver imap_driver = { imap_create_box, imap_open_box, imap_get_uidnext, + imap_get_supported_flags, imap_confirm_box_empty, imap_delete_box, imap_finish_delete_box, diff --git a/src/drv_maildir.c b/src/drv_maildir.c @@ -1325,6 +1325,12 @@ maildir_get_uidnext( store_t *gctx ATTR_UNUSED ) return 0; } +static xint +maildir_get_supported_flags( store_t *gctx ATTR_UNUSED ) +{ + return 255; +} + static void maildir_create_box( store_t *gctx, void (*cb)( int sts, void *aux ), void *aux ) @@ -1934,6 +1940,7 @@ struct driver maildir_driver = { maildir_create_box, maildir_open_box, maildir_get_uidnext, + maildir_get_supported_flags, maildir_confirm_box_empty, maildir_delete_box, maildir_finish_delete_box, diff --git a/src/sync.c b/src/sync.c @@ -170,6 +170,7 @@ typedef struct { uint newuidval[2]; // UID validity obtained from driver uint newuid[2]; // TUID lookup makes sense only for UIDs >= this uint mmaxxuid; // highest expired UID on master + uchar good_flags[2], bad_flags[2]; } sync_vars_t; static void sync_ref( sync_vars_t *svars ) { ++svars->ref_count; } @@ -282,6 +283,24 @@ match_tuids( sync_vars_t *svars, int t, message_t *msgs ) } +static uchar +sanitize_flags( uchar tflags, sync_vars_t *svars, int t ) +{ + if (!(DFlags & QUIET)) { + // We complain only once per flag per store - even though _theoretically_ + // each mailbox can support different flags according to the IMAP spec. + uchar bflags = tflags & ~(svars->good_flags[t] | svars->bad_flags[t]); + if (bflags) { + char bfbuf[16]; + make_flags( bflags, bfbuf ); + notice( "Notice: %s does not support flag(s) '%s'; not propagating.\n", str_ms[t], bfbuf ); + svars->bad_flags[t] |= bflags; + } + } + return tflags & svars->good_flags[t]; +} + + typedef struct copy_vars { void (*cb)( int sts, uint uid, struct copy_vars *vars ); void *aux; @@ -421,7 +440,7 @@ msg_fetched( int sts, void *aux ) return; } - vars->msg->flags = vars->data.flags; + vars->msg->flags = vars->data.flags = sanitize_flags( vars->data.flags, svars, t ); scr = (svars->drv[1-t]->get_caps( svars->ctx[1-t] ) / DRV_CRLF) & 1; tcr = (svars->drv[t]->get_caps( svars->ctx[t] ) / DRV_CRLF) & 1; @@ -1490,6 +1509,8 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux } info( "Synchronizing...\n" ); + for (t = 0; t < 2; t++) + svars->good_flags[t] = (uchar)svars->drv[t]->get_supported_flags( svars->ctx[t] ); debug( "synchronizing old entries\n" ); for (srec = svars->srecs; srec; srec = srec->next) { @@ -1550,7 +1571,7 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux } else { // We have a source. The target may be in an unknown state. if (svars->chan->ops[t] & OP_FLAGS) { - sflags = srec->msg[1-t]->flags; + sflags = sanitize_flags( srec->msg[1-t]->flags, svars, t ); if ((t == M) && (srec->status & (S_EXPIRE|S_EXPIRED))) { /* Don't propagate deletion resulting from expiration. */ debug( " slave expiring\n" );