From 7d151fd0919f6cc7ba9cdc1e7de90697c66dce43 Mon Sep 17 00:00:00 2001 From: Mehdi Abaakouk Date: Tue, 21 Feb 2017 10:50:35 +0100 Subject: [PATCH] Share label editing between imap/notmuch --- curs_main.c | 140 +++++++++++++++++++++++-------------------------- imap/imap.c | 113 ++++++++++++++++++++++++++++++++++----- imap/imap.h | 1 - imap/message.c | 115 ---------------------------------------- mbox.c | 4 ++ mh.c | 4 ++ mutt_notmuch.c | 11 +++- mutt_notmuch.h | 1 - mutt_tags.c | 22 ++++++++ mutt_tags.h | 2 + mx.h | 2 + nntp.c | 2 + pop.c | 2 + 13 files changed, 213 insertions(+), 206 deletions(-) diff --git a/curs_main.c b/curs_main.c index e271c7a18..d603c5400 100644 --- a/curs_main.c +++ b/curs_main.c @@ -47,6 +47,7 @@ #include "mutt_curses.h" #include "mutt_menu.h" #include "mutt_socket.h" +#include "mutt_tags.h" #include "mx.h" #include "ncrypt/ncrypt.h" #include "opcodes.h" @@ -1862,100 +1863,91 @@ int mutt_index_menu(void) mutt_message(_("No virtual folder, aborting.")); break; } -#ifdef USE_IMAP - if (Context->magic == MUTT_IMAP) + CHECK_MSGCOUNT; + CHECK_VISIBLE; + CHECK_READONLY; + + rc = hdr_tags_editor(Context, tag ? NULL : hdr_tags_get_with_hidden(CURHDR), buf); + if (rc < 0) + break; + else if (rc == 0) { - CHECK_MSGCOUNT; - CHECK_VISIBLE; - CHECK_READONLY; - rc = imap_keywords_message(tag ? NULL : CURHDR, op == OP_MAIN_MODIFY_LABELS_THEN_HIDE); - if (rc > 0) - { - Context->changed = 1; - menu->redraw = REDRAW_FULL; - mutt_message(_("%d keywords changed."), rc); - } - else if (rc == 0) - mutt_message(_("No keywords changed.")); + mutt_message(_("No label specified, aborting.")); break; } -#endif -#ifdef USE_NOTMUCH - if (Context->magic == MUTT_NOTMUCH) + + if (tag) { - CHECK_MSGCOUNT; - CHECK_VISIBLE; - *buf = '\0'; - if (mutt_get_field("Add/remove labels: ", buf, sizeof(buf), MUTT_NM_TAG) || !*buf) + char msgbuf[STRING]; + struct Progress progress; + int px; + + if (!Context->quiet) { - mutt_message(_("No label specified, aborting.")); - break; + snprintf(msgbuf, sizeof(msgbuf), _("Update labels...")); + mutt_progress_init(&progress, msgbuf, MUTT_PROGRESS_MSG, 1, Context->tagged); } - if (tag) - { - char msgbuf[STRING]; - struct Progress progress; - int px; - if (!Context->quiet) - { - snprintf(msgbuf, sizeof(msgbuf), _("Update labels...")); - mutt_progress_init(&progress, msgbuf, MUTT_PROGRESS_MSG, 1, - Context->tagged); - } +#ifdef USE_NOTMUCH + if (Context->magic == MUTT_NOTMUCH) nm_longrun_init(Context, true); - for (px = 0, j = 0; j < Context->vcount; j++) +#endif + for (px = 0, j = 0; j < Context->vcount; j++) + { + if (Context->hdrs[Context->v2r[j]]->tagged) { - if (Context->hdrs[Context->v2r[j]]->tagged) + if (!Context->quiet) + mutt_progress_update(&progress, ++px, -1); + hdr_tags_commit(Context, Context->hdrs[Context->v2r[j]], buf); + if (op == OP_MAIN_MODIFY_LABELS_THEN_HIDE) { - if (!Context->quiet) - mutt_progress_update(&progress, ++px, -1); - nm_modify_message_tags(Context, Context->hdrs[Context->v2r[j]], buf); - bool still_queried = - nm_message_is_still_queried(Context, Context->hdrs[Context->v2r[j]]); - if (op == OP_MAIN_MODIFY_LABELS_THEN_HIDE) - { - Context->hdrs[Context->v2r[j]]->quasi_deleted = !still_queried; - Context->changed = true; - } + bool still_queried = false; +#ifdef USE_NOTMUCH + if (Context->magic == MUTT_NOTMUCH) + still_queried = nm_message_is_still_queried( + Context, Context->hdrs[Context->v2r[j]]); +#endif + Context->hdrs[Context->v2r[j]]->quasi_deleted = !still_queried; + Context->changed = true; } } + } +#ifdef USE_NOTMUCH + if (Context->magic == MUTT_NOTMUCH) nm_longrun_done(Context); - menu->redraw = REDRAW_STATUS | REDRAW_INDEX; +#endif + menu->redraw = REDRAW_STATUS | REDRAW_INDEX; + } + else + { + if (hdr_tags_commit(Context, CURHDR, buf)) + { + mutt_message(_("Failed to modify labels, aborting.")); + break; } - else + if (op == OP_MAIN_MODIFY_LABELS_THEN_HIDE) { - if (nm_modify_message_tags(Context, CURHDR, buf)) - { - mutt_message(_("Failed to modify labels, aborting.")); - break; - } - if (op == OP_MAIN_MODIFY_LABELS_THEN_HIDE) - { - bool still_queried = nm_message_is_still_queried(Context, CURHDR); - CURHDR->quasi_deleted = !still_queried; - Context->changed = true; - } - if (menu->menu == MENU_PAGER) - { - op = OP_DISPLAY_MESSAGE; - continue; - } - if (option(OPT_RESOLVE)) + CURHDR->quasi_deleted = true; + Context->changed = true; + } + if (menu->menu == MENU_PAGER) + { + op = OP_DISPLAY_MESSAGE; + continue; + } + if (option(OPT_RESOLVE)) + { + if ((menu->current = ci_next_undeleted(menu->current)) == -1) { - if ((menu->current = ci_next_undeleted(menu->current)) == -1) - { - menu->current = menu->oldcurrent; - menu->redraw = REDRAW_CURRENT; - } - else - menu->redraw = REDRAW_MOTION_RESYNCH; + menu->current = menu->oldcurrent; + menu->redraw = REDRAW_CURRENT; } else - menu->redraw = REDRAW_CURRENT; + menu->redraw = REDRAW_MOTION_RESYNCH; } + else + menu->redraw = REDRAW_CURRENT; } -#endif menu->redraw |= REDRAW_STATUS; break; } diff --git a/imap/imap.c b/imap/imap.c index e5925960d..0504afd95 100644 --- a/imap/imap.c +++ b/imap/imap.c @@ -1258,8 +1258,89 @@ static int sync_helper(struct ImapData *idata, int right, int flag, const char * return count; } + +/* imap_edit_message_tags: Prompt and validate new messages tags + * + * @retval 0: no valid user input + * @retval 1: buf set + **/ +static int imap_edit_message_tags(struct Context *ctx, const char *tags, char *buf) +{ + char *new = NULL; + char *checker = NULL; + struct ImapData* idata = (struct ImapData*) ctx->data; + + /* Check for \* flags capability */ + if (!imap_has_flag(&idata->flags, NULL)) + { + mutt_error(_("IMAP server doesn't support custom keywords")); + return -1; + } + + *buf = '\0'; + if (tags) + strncpy(buf, tags, LONG_STRING); + + if (mutt_get_field("Keywords: ", buf, LONG_STRING, 0) != 0) + return -1; + + /* each keyword must be atom defined by rfc822 as: + * + * atom = 1* + * CHAR = ( 0.-127. ) + * specials = "(" / ")" / "<" / ">" / "@" + * / "," / ";" / ":" / "\" / <"> + * / "." / "[" / "]" + * SPACE = ( 32. ) + * CTLS = ( 0.-31., 127.) + * + * And must be separated by one space. + */ + + new = buf; + checker = buf; + SKIPWS(checker); + while (*checker != '\0') + { + if (*checker < 32 || *checker >= 127 || // We allow space because it's the separator + *checker == 40 || // ( + *checker == 41 || // ) + *checker == 60 || // < + *checker == 62 || // > + *checker == 64 || // @ + *checker == 44 || // , + *checker == 59 || // ; + *checker == 58 || // : + *checker == 92 || // backslash + *checker == 34 || // " + *checker == 46 || // . + *checker == 91 || // [ + *checker == 93) // ] + { + mutt_error(_("Invalid IMAP keyworks")); + mutt_sleep(2); + return 0; + } + + /* Skip duplicate space */ + while (*checker == ' ' && *(checker + 1) == ' ') + checker++; + + /* copy char to new and go the next one */ + *new ++ = *checker++; + } + *new = '\0'; + new = buf; /* rewind */ + mutt_remove_trailing_ws(new); + + if (mutt_strcmp(tags, buf) == 0) + return 0; + return 1; +} + + /** - * imap_sync_keywords - Add/Change/Remove keywords from headers + * imap_commit_message_tags - Add/Change/Remove keywords from headers * @param idata: pointer to a struct ImapData * @param h: pointer to a header struct * @@ -1276,11 +1357,17 @@ static int sync_helper(struct ImapData *idata, int right, int flag, const char * * Also this method check that each keywords is support by the server * first and remove unsupported one. */ -static int imap_sync_keywords(struct ImapData *idata, struct Header *h) +static int imap_commit_message_tags(struct Context *ctx, struct Header *h, char *tags) { + struct ImapData *idata = NULL; struct Buffer *cmd = NULL; char uid[11]; + idata = ctx->data; + + if (*tags == '\0') + tags = NULL; + if (!mutt_bit_isset(idata->ctx->rights, MUTT_ACL_WRITE)) return 0; @@ -1291,7 +1378,7 @@ static int imap_sync_keywords(struct ImapData *idata, struct Header *h) { if (!(cmd = mutt_buffer_new())) { - mutt_debug(1, "imap_sync_keywords: unable to allocate buffer\n"); + mutt_debug(1, "imap_commit_message_tags: unable to allocate buffer\n"); return -1; } cmd->dptr = cmd->data; @@ -1311,30 +1398,32 @@ static int imap_sync_keywords(struct ImapData *idata, struct Header *h) } /* Add new custom keywords */ - if (hdr_tags_get_with_hidden(h)) + if (tags) { if (!(cmd = mutt_buffer_new())) { - mutt_debug(1, "imap_sync_keywords: fail to remove old keywords\n"); + mutt_debug(1, "imap_commit_message_tags: fail to remove old keywords\n"); return -1; } cmd->dptr = cmd->data; mutt_buffer_addstr(cmd, "UID STORE "); mutt_buffer_addstr(cmd, uid); mutt_buffer_addstr(cmd, " +FLAGS.SILENT ("); - mutt_buffer_addstr(cmd, hdr_tags_get_with_hidden(h)); + mutt_buffer_addstr(cmd, tags); mutt_buffer_addstr(cmd, ")"); if (imap_exec(idata, cmd->data, 0) != 0) { - mutt_debug(1, "imap_sync_keywords: fail to add new keywords\n"); + mutt_debug(1, "imap_commit_message_tags: fail to add new keywords\n"); return -1; } mutt_buffer_free(&cmd); } - /* We are good sync them and only keep those supported by the server */ + /* We are good sync them */ + mutt_debug(1, "NEW TAGS: %d\n", tags); + hdr_tags_replace(h, tags); FREE(&HEADER_DATA(h)->keywords_remote); HEADER_DATA(h)->keywords_remote = safe_strdup(hdr_tags_get_with_hidden(h)); return 0; @@ -1411,12 +1500,6 @@ int imap_sync_mailbox(struct Context *ctx, int expunge) #endif } - if (mutt_strcmp(HEADER_DATA(h)->keywords_remote, hdr_tags_get_with_hidden(h)) != 0) - { - if (imap_sync_keywords(idata, h) != 0) - mutt_error(_("Error syncing keywords")); - } - if (h->active && h->changed) { #ifdef USE_HCACHE @@ -2454,4 +2537,6 @@ struct MxOps mx_imap_ops = { .open_new_msg = imap_open_new_message, .check = imap_check_mailbox_reopen, .sync = NULL, /* imap syncing is handled by imap_sync_mailbox */ + .edit_msg_tags = imap_edit_message_tags, + .commit_msg_tags = imap_commit_message_tags, }; diff --git a/imap/imap.h b/imap/imap.h index 2799920f0..f9c660fa4 100644 --- a/imap/imap.h +++ b/imap/imap.h @@ -69,7 +69,6 @@ int imap_mailbox_rename(const char *mailbox); /* message.c */ int imap_append_message(struct Context *ctx, struct Message *msg); int imap_copy_messages(struct Context *ctx, struct Header *h, char *dest, int delete); -int imap_keywords_message(struct Header *h, bool quasi_deleted); /* socket.c */ void imap_logout_all(void); diff --git a/imap/message.c b/imap/message.c index 02512328f..23778adda 100644 --- a/imap/message.c +++ b/imap/message.c @@ -477,121 +477,6 @@ static void imap_generate_seqset(struct Buffer *b, struct ImapData *idata, } } -/** - * imap_keywords_message - Add/Change/Remove keywords from headers - * @param[in] h: pointer to a header struct or NULL to modify - * all headers of the Context - * - * @return int the number of headers changed - * @retval -1 in case of error - * - * This method prompts the user to edit keywords of current header - * And update keywords_local with the new keywords list - * - * It also ensure each keyword are valid IMAP rfc822 atom - */ -int imap_keywords_message(struct Header *h, bool quasi_deleted) -{ - char buf[LONG_STRING], *new, *checker; - int i; - int changed; - - if (!Context || Context->magic != MUTT_IMAP) - return -1; - - /* Check for \* flags capability */ - struct ImapData* idata = (struct ImapData*) Context->data; - if (!imap_has_flag(&idata->flags, NULL)) - { - mutt_error(_("IMAP server doesn't support custom keywords")); - return -1; - } - - *buf = '\0'; - if (h && hdr_tags_get(h)) - { - strncpy(buf, hdr_tags_get_with_hidden(h), LONG_STRING); - } - - if (mutt_get_field("Keywords: ", buf, sizeof(buf), 0) != 0) - return 0; - - /* each keyword must be atom defined by rfc822 as: - * - * atom = 1* - * CHAR = ( 0.-127. ) - * specials = "(" / ")" / "<" / ">" / "@" - * / "," / ";" / ":" / "\" / <"> - * / "." / "[" / "]" - * SPACE = ( 32. ) - * CTLS = ( 0.-31., 127.) - * - * And must be separated by one space. - */ - - new = buf; - checker = buf; - SKIPWS(checker); - while (*checker != '\0') - { - if (*checker < 32 || *checker >= 127 || // We allow space because it's the separator - *checker == 40 || // ( - *checker == 41 || // ) - *checker == 60 || // < - *checker == 62 || // > - *checker == 64 || // @ - *checker == 44 || // , - *checker == 59 || // ; - *checker == 58 || // : - *checker == 92 || // backslash - *checker == 34 || // " - *checker == 46 || // . - *checker == 91 || // [ - *checker == 93) // ] - { - mutt_error(_("Invalid IMAP keyworks")); - mutt_sleep(2); - return 0; - } - - /* Skip duplicate space */ - while (*checker == ' ' && *(checker + 1) == ' ') - checker++; - - /* copy char to new and go the next one */ - *new ++ = *checker++; - } - *new = '\0'; - new = buf; /* rewind */ - mutt_remove_trailing_ws(new); - - if (*new == '\0') - new = NULL; - - changed = 0; - if (h != NULL) - { - changed += hdr_tags_replace(h, new); - if (changed) - h->quasi_deleted = quasi_deleted; - } - else - { -#define HDR_OF(index) Context->hdrs[Context->v2r[(index)]] - for (i = 0; i < Context->vcount; ++i) - { - if (HDR_OF(i)->tagged && hdr_tags_replace(HDR_OF(i), new)) - { - HDR_OF(i)->quasi_deleted = quasi_deleted; - ++changed; - mutt_set_flag(Context, HDR_OF(i), MUTT_TAG, 0); - } - } - } - - return changed; -} - /* * imap_read_headers - Read headers from the server * diff --git a/mbox.c b/mbox.c index 1fecf7024..b583be3a7 100644 --- a/mbox.c +++ b/mbox.c @@ -1411,6 +1411,8 @@ struct MxOps mx_mbox_ops = { .open_new_msg = mbox_open_new_message, .check = mbox_check_mailbox, .sync = mbox_sync_mailbox, + .edit_msg_tags = NULL, + .commit_msg_tags = NULL, }; struct MxOps mx_mmdf_ops = { @@ -1423,4 +1425,6 @@ struct MxOps mx_mmdf_ops = { .open_new_msg = mbox_open_new_message, .check = mbox_check_mailbox, .sync = mbox_sync_mailbox, + .edit_msg_tags = NULL, + .commit_msg_tags = NULL, }; diff --git a/mh.c b/mh.c index d39734da7..074b321dd 100644 --- a/mh.c +++ b/mh.c @@ -2612,6 +2612,8 @@ struct MxOps mx_maildir_ops = { .open_new_msg = maildir_open_new_message, .check = maildir_check_mailbox, .sync = mh_sync_mailbox, + .edit_msg_tags = NULL, + .commit_msg_tags = NULL, }; struct MxOps mx_mh_ops = { @@ -2624,4 +2626,6 @@ struct MxOps mx_mh_ops = { .open_new_msg = mh_open_new_message, .check = mh_check_mailbox, .sync = mh_sync_mailbox, + .edit_msg_tags = NULL, + .commit_msg_tags = NULL, }; diff --git a/mutt_notmuch.c b/mutt_notmuch.c index 22929fcef..9234eb30a 100644 --- a/mutt_notmuch.c +++ b/mutt_notmuch.c @@ -1847,7 +1847,14 @@ void nm_query_window_backward(void) mutt_debug(2, "nm_query_window_backward (%d)\n", NmQueryWindowCurrentPosition); } -int nm_modify_message_tags(struct Context *ctx, struct Header *hdr, char *buf) +static int nm_edit_message_tags(struct Context *ctx, const char *tags, char *buf) +{ + *buf = '\0'; + return (mutt_get_field("Add/remove labels: ", buf, sizeof(buf), MUTT_NM_TAG) || !*buf); +} + + +static int nm_commit_message_tags(struct Context *ctx, struct Header *hdr, char *buf) { struct NmCtxData *data = get_ctxdata(ctx); notmuch_database_t *db = NULL; @@ -2530,4 +2537,6 @@ struct MxOps mx_notmuch_ops = { .close_msg = nm_close_message, .commit_msg = nm_commit_message, .open_new_msg = NULL, + .edit_msg_tags = nm_edit_message_tags, + .commit_msg_tags = nm_commit_message_tags, }; diff --git a/mutt_notmuch.h b/mutt_notmuch.h index 6a53bfa88..9a6a41359 100644 --- a/mutt_notmuch.h +++ b/mutt_notmuch.h @@ -36,7 +36,6 @@ char *nm_header_get_folder(struct Header *h); int nm_update_filename(struct Context *ctx, const char *old, const char *new, struct Header *h); bool nm_normalize_uri(char *new_uri, const char *orig_uri, size_t new_uri_sz); char *nm_uri_from_query(struct Context *ctx, char *buf, size_t bufsz); -int nm_modify_message_tags(struct Context *ctx, struct Header *hdr, char *buf); bool nm_message_is_still_queried(struct Context *ctx, struct Header *hdr); void nm_query_window_backward(void); diff --git a/mutt_tags.c b/mutt_tags.c index 02fe1546e..8493f85ec 100644 --- a/mutt_tags.c +++ b/mutt_tags.c @@ -19,10 +19,12 @@ #include "config.h" #include "mutt_tags.h" +#include "context.h" #include "globals.h" #include "lib/hash.h" #include "lib/string2.h" #include "header.h" +#include "mx.h" #define HEADER_TAGS(ph) ((struct HeaderTags *) ((ph)->tags)) @@ -218,3 +220,23 @@ int hdr_tags_replace(struct Header *h, char *tags) } return 1; } + + +int hdr_tags_editor(struct Context *ctx, const char *tags, char *buf) +{ + if (ctx->mx_ops->edit_msg_tags) + return ctx->mx_ops->edit_msg_tags(ctx, tags, buf); + + mutt_message(_("Folder doesn't support tagging, aborting.")); + return -1; +} + + +int hdr_tags_commit(struct Context *ctx, struct Header *h, char *tags) +{ + if (ctx->mx_ops->commit_msg_tags) + return ctx->mx_ops->commit_msg_tags(ctx, h, tags); + + mutt_message(_("Folder doesn't support tagging, aborting.")); + return -1; +} diff --git a/mutt_tags.h b/mutt_tags.h index af5869678..2403d8165 100644 --- a/mutt_tags.h +++ b/mutt_tags.h @@ -64,5 +64,7 @@ const char *hdr_tags_get_transformed_for(char *name, struct Header *h); void hdr_tags_init(struct Header *h); void hdr_tags_add(struct Header *h, char *new_tag); int hdr_tags_replace(struct Header *h, char *tags); +int hdr_tags_editor(struct Context *ctx, const char *tags, char *buf); +int hdr_tags_commit(struct Context *ctx, struct Header *h, char *tags); #endif /* _MUTT_TAG_H */ diff --git a/mx.h b/mx.h index a5b479d03..6e91e17f2 100644 --- a/mx.h +++ b/mx.h @@ -63,6 +63,8 @@ struct MxOps int (*close_msg)(struct Context *ctx, struct Message *msg); int (*commit_msg)(struct Context *ctx, struct Message *msg); int (*open_new_msg)(struct Message *msg, struct Context *ctx, struct Header *hdr); + int (*edit_msg_tags)(struct Context *ctx, const char *tags, char *buf); + int (*commit_msg_tags)(struct Context *msg, struct Header *hdr, char *buf); }; /** diff --git a/nntp.c b/nntp.c index f0b72f040..37a75c913 100644 --- a/nntp.c +++ b/nntp.c @@ -2527,4 +2527,6 @@ struct MxOps mx_nntp_ops = { .close_msg = nntp_close_message, .commit_msg = NULL, .open_new_msg = NULL, + .edit_msg_tags = NULL, + .commit_msg_tags = NULL, }; diff --git a/pop.c b/pop.c index 2847b0e5e..4647665fb 100644 --- a/pop.c +++ b/pop.c @@ -979,4 +979,6 @@ struct MxOps mx_pop_ops = { .commit_msg = NULL, .open_new_msg = NULL, .sync = pop_sync_mailbox, + .edit_msg_tags = NULL, + .commit_msg_tags = NULL, }; -- 2.40.0