# HG changeset patch # User David Champion <[email protected]> # Date 1451647488 21600 # Fri Jan 01 05:24:48 2016 -0600 # Node ID 19d38f8f81fb41dc88e20a8295db39d953371e7a # Parent cd7be2bfff1cd15592b15adc21f44f7b50ecc312 add list actions for remaining RFC 2369 List-* headers
As part of enhancing RFC 2369 support, we now add the <list-action> function. When <list-action> is invoked, a new menu opens showing all available list actions specified by RFC 2369. It might look similar to this: | q:Exit r:Reply p:Post s:Subscribe u:Unsubscribe | | Select a list action: | | Help: mailto:[email protected]?subject=help | Post: mailto:[email protected] | Reply: -- | Subscribe: mailto:[email protected]?subject=subscribe%20listname | Unsubscribe: mailto:[email protected]?subject=unsubscribe%20listname | Archives: -- This displays the values of List-* headers from the current message. The user may select one of these menu options using the usual menu navigation, or by pressing a bound key (see help). On doing so, if the action is a mailto: URI, a new draft message is created as specified. Most pertinently, this makes it easier for Mutt users to subscribe to or unsubscribe from a list message that their mutt configuration does not already know about. However there's no good reason not to support all List-* headers in the same general framework. (RFC 2369 allows HTTP URIs in List-* headers. These are not presently supported, but could be if mutt were to provide more general support for opening URLs.) The default binding to bring up the <list-action> menu is ESC-L (capital L). diff -r cd7be2bfff1c -r 19d38f8f81fb Makefile.am --- a/Makefile.am Fri Jan 01 05:19:48 2016 -0600 +++ b/Makefile.am Fri Jan 01 05:24:48 2016 -0600 @@ -34,7 +34,7 @@ score.c send.c sendlib.c signal.c sort.c \ status.c system.c thread.c charset.c history.c lib.c \ muttlib.c editmsg.c mbyte.c mutt_idna.c \ - url.c ascii.c crypt-mod.c crypt-mod.h safe_asprintf.c + url.c ascii.c crypt-mod.c crypt-mod.h safe_asprintf.c listmenu.c nodist_mutt_SOURCES = $(BUILT_SOURCES) diff -r cd7be2bfff1c -r 19d38f8f81fb OPS --- a/OPS Fri Jan 01 05:19:48 2016 -0600 +++ b/OPS Fri Jan 01 05:24:48 2016 -0600 @@ -93,7 +93,14 @@ OP_HELP "this screen" OP_JUMP "jump to an index number" OP_LAST_ENTRY "move to the last entry" +OP_LIST_ACTION "perform mailing list action" +OP_LIST_ARCHIVE "retrieve list archive information" +OP_LIST_HELP "retrieve list help" +OP_LIST_OWNER "contact list owner" +OP_LIST_POST "post to mailing list" OP_LIST_REPLY "reply to specified mailing list" +OP_LIST_SUBSCRIBE "subscribe to mailing list" +OP_LIST_UNSUBSCRIBE "unsubscribe from mailing list" OP_MACRO "execute a macro" OP_MAIL "compose a new mail message" OP_MAIN_BREAK_THREAD "break the thread in two" diff -r cd7be2bfff1c -r 19d38f8f81fb curs_main.c --- a/curs_main.c Fri Jan 01 05:19:48 2016 -0600 +++ b/curs_main.c Fri Jan 01 05:24:48 2016 -0600 @@ -2045,6 +2045,12 @@ menu->redraw = REDRAW_FULL; break; + case OP_LIST_ACTION: + + mutt_list_menu (Context, CURHDR); + menu->redraw = REDRAW_FULL; + break; + case OP_LIST_REPLY: CHECK_ATTACH; diff -r cd7be2bfff1c -r 19d38f8f81fb functions.h --- a/functions.h Fri Jan 01 05:19:48 2016 -0600 +++ b/functions.h Fri Jan 01 05:24:48 2016 -0600 @@ -115,7 +115,8 @@ { "previous-undeleted", OP_MAIN_PREV_UNDELETED, "k" }, { "limit", OP_MAIN_LIMIT, "l" }, { "link-threads", OP_MAIN_LINK_THREADS, "&" }, - { "list-reply", OP_LIST_REPLY, "L" }, + { "list-action", OP_LIST_ACTION, "\033L" }, + { "list-reply", OP_LIST_REPLY, "L" }, { "mail", OP_MAIL, "m" }, { "toggle-new", OP_TOGGLE_NEW, "N" }, { "toggle-write", OP_TOGGLE_WRITE, "%" }, @@ -201,7 +202,8 @@ { "previous-undeleted",OP_MAIN_PREV_UNDELETED, "k" }, { "previous-entry", OP_PREV_ENTRY, "K" }, { "link-threads", OP_MAIN_LINK_THREADS, "&" }, - { "list-reply", OP_LIST_REPLY, "L" }, + { "list-action", OP_LIST_ACTION, "\033L" }, + { "list-reply", OP_LIST_REPLY, "L" }, { "redraw-screen", OP_REDRAW, "\014" }, { "mail", OP_MAIL, "m" }, { "mark-as-new", OP_TOGGLE_NEW, "N" }, @@ -286,7 +288,8 @@ { "reply", OP_REPLY, "r" }, { "resend-message", OP_RESEND, "\033e" }, { "group-reply", OP_GROUP_REPLY, "g" }, - { "list-reply", OP_LIST_REPLY, "L" }, + { "list-action", OP_LIST_ACTION, "\033L" }, + { "list-reply", OP_LIST_REPLY, "L" }, { "forward-message", OP_FORWARD_MESSAGE, "f" }, { "view-text", OP_ATTACH_VIEW_TEXT, "T" }, { "view-attach", OP_VIEW_ATTACH, M_ENTER_S }, @@ -432,6 +435,18 @@ +const struct binding_t OpList[] = { /* map: list */ + { "list-archive", OP_LIST_ARCHIVE, "a" }, + { "list-help", OP_LIST_HELP, "h" }, + { "list-owner", OP_LIST_OWNER, "o" }, + { "list-post", OP_LIST_POST, "p" }, + { "list-reply", OP_LIST_REPLY, "r" }, + { "list-subscribe", OP_LIST_SUBSCRIBE, "s" }, + { "list-unsubscribe", OP_LIST_UNSUBSCRIBE, "u" }, + { NULL, 0, NULL } +}; + + /* When using the GPGME based backend we have some useful functions for the SMIME menu. */ const struct binding_t OpSmime[] = { /* map: smime */ diff -r cd7be2bfff1c -r 19d38f8f81fb keymap.c --- a/keymap.c Fri Jan 01 05:19:48 2016 -0600 +++ b/keymap.c Fri Jan 01 05:24:48 2016 -0600 @@ -42,6 +42,7 @@ { "browser", MENU_FOLDER }, { "compose", MENU_COMPOSE }, { "editor", MENU_EDITOR }, + { "list", MENU_LIST }, { "index", MENU_MAIN }, { "pager", MENU_PAGER }, { "postpone", MENU_POST }, @@ -721,6 +722,7 @@ create_bindings (OpAttach, MENU_ATTACH); create_bindings (OpBrowser, MENU_FOLDER); create_bindings (OpCompose, MENU_COMPOSE); + create_bindings (OpList, MENU_LIST); create_bindings (OpMain, MENU_MAIN); create_bindings (OpPager, MENU_PAGER); create_bindings (OpPost, MENU_POST); @@ -959,6 +961,8 @@ return OpEditor; case MENU_QUERY: return OpQuery; + case MENU_LIST: + return OpList; case MENU_PGP: return (WithCrypto & APPLICATION_PGP)? OpPgp:NULL; diff -r cd7be2bfff1c -r 19d38f8f81fb keymap.h --- a/keymap.h Fri Jan 01 05:19:48 2016 -0600 +++ b/keymap.h Fri Jan 01 05:24:48 2016 -0600 @@ -59,6 +59,7 @@ MENU_EDITOR, MENU_FOLDER, MENU_GENERIC, + MENU_LIST, MENU_MAIN, MENU_PAGER, MENU_POST, diff -r cd7be2bfff1c -r 19d38f8f81fb listmenu.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/listmenu.c Fri Jan 01 05:24:48 2016 -0600 @@ -0,0 +1,191 @@ +/* + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include "mutt.h" +#include "mutt_curses.h" +#include "mutt_menu.h" +#include "url.h" + +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include <locale.h> + +static const struct mapping_t ListHelp[] = { + { N_("Exit"), OP_EXIT }, + { N_("Reply"), OP_LIST_REPLY }, + { N_("Post"), OP_LIST_POST }, + { N_("Subscribe"), OP_LIST_SUBSCRIBE }, + { N_("Unsubscribe"), OP_LIST_UNSUBSCRIBE }, + { NULL, 0 } +}; + +static const struct mapping_t ListActions[] = { + { N_("Help"), offsetof(ENVELOPE, list_help) }, + { N_("Post"), offsetof(ENVELOPE, list_post) }, + { N_("Reply"), offsetof(ENVELOPE, list_reply) }, + { N_("Subscribe"), offsetof(ENVELOPE, list_subscribe) }, + { N_("Unsubscribe"), offsetof(ENVELOPE, list_unsubscribe) }, + { N_("Archives"), offsetof(ENVELOPE, list_archive) }, + { NULL, 0 } +}; + +#if 0 /* notused - development - remove for final commit */ +static void draw_list_action (int idx, char *s) +{ + mvprintw (idx+2, 2, "%14s: ", ListActions[idx].name); + mutt_paddstr (COLS - 20, s); +} + +static void draw_actions (MUTTMENU *menu, HEADER *msg) +{ + int i; + + for (i = 0; ListActions[i].name; i++) + { + char **value = (char **)( ((caddr_t) msg->env) + ListActions[i].value); + draw_list_action(i, *value); + } + + menu->offset = i + 2; + menu->pagelen = LINES - i - 2; +} +#endif + +static void make_entry (char *b, size_t blen, MUTTMENU *menu, int num) +{ + char **value; + HEADER *msg = menu->data; + + value = (char **)( ((caddr_t) msg->env) + ListActions[num].value); + snprintf (b, blen, " %14s: %s", ListActions[num].name, *value ? *value : "--"); +} + +static int list_action (CONTEXT *ctx, HEADER *msg, struct mapping_t *action) +{ + HEADER *newmsg = NULL; + char *body = NULL; + char **address = NULL; + + address = (char **)( ((caddr_t) msg->env) + action->value); + if (address == NULL || *address == '\0') + { + mutt_error (_("No list action available for %s."), action->name); + return 0; + } + + if (url_check_scheme (*address) != U_MAILTO) + { + mutt_error (_("List actions only support mailto: URIs. (Try a browser?)")); + return 1; + } + + newmsg = mutt_new_header (); + newmsg->env = mutt_new_envelope (); + if (url_parse_mailto (newmsg->env, &body, *address) < 0) + { + mutt_error (_("Could not parse mailto: URI.")); + return 1; + } + + ci_send_message (0, newmsg, NULL, ctx, msg); + return 1; +} + +void mutt_list_menu (CONTEXT *ctx, HEADER *msg) +{ + MUTTMENU *menu = NULL; + int done = 0; + char helpstr[LONG_STRING]; + +#if 0 + helpstr[0] = 0; + mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_PGP, OP_EXIT); + strcat (helpstr, buf); /* __STRCAT_CHECKED__ */ + mutt_make_help (buf, sizeof (buf), _("Select "), MENU_PGP, + OP_GENERIC_SELECT_ENTRY); + strcat (helpstr, buf); /* __STRCAT_CHECKED__ */ + mutt_make_help (buf, sizeof (buf), _("Check key "), MENU_PGP, OP_VERIFY_KEY); + strcat (helpstr, buf); /* __STRCAT_CHECKED__ */ + mutt_make_help (buf, sizeof (buf), _("Help"), MENU_PGP, OP_HELP); + strcat (helpstr, buf); /* __STRCAT_CHECKED__ */ +#endif + + menu = mutt_new_menu (MENU_LIST); + menu->max = (sizeof(ListActions) / sizeof(struct mapping_t)) - 1; + menu->offset = 4; + menu->make_entry = make_entry; + menu->data = msg; + menu->title = "Available mailing list actions"; + menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_LIST, ListHelp); + + while (!done) + { + switch (mutt_menuLoop (menu)) + { + + case OP_REDRAW: + /* draw_actions(menu, msg); */ + mvprintw (2, 0, _("Select a list action:")); + menu->offset = 4; + break; + + case OP_LIST_HELP: + done = list_action(ctx, msg, (struct mapping_t *)&ListActions[0]); + break; + + case OP_LIST_POST: + done = list_action(ctx, msg, (struct mapping_t *)&ListActions[1]); + break; + + case OP_LIST_REPLY: + done = list_action(ctx, msg, (struct mapping_t *)&ListActions[2]); + break; + + case OP_LIST_SUBSCRIBE: + done = list_action(ctx, msg, (struct mapping_t *)&ListActions[3]); + break; + + case OP_LIST_UNSUBSCRIBE: + done = list_action(ctx, msg, (struct mapping_t *)&ListActions[4]); + break; + + case OP_LIST_ARCHIVE: + done = list_action(ctx, msg, (struct mapping_t *)&ListActions[5]); + break; + + case OP_GENERIC_SELECT_ENTRY: + done = list_action(ctx, msg, (struct mapping_t *)&ListActions[menu->current]); + break; + + case OP_EXIT: + done = 1; + break; + } + } + + mutt_menuDestroy (&menu); + set_option (OPTNEEDREDRAW); + return; +} diff -r cd7be2bfff1c -r 19d38f8f81fb mutt.h --- a/mutt.h Fri Jan 01 05:19:48 2016 -0600 +++ b/mutt.h Fri Jan 01 05:24:48 2016 -0600 @@ -586,8 +586,13 @@ ADDRESS *sender; ADDRESS *reply_to; ADDRESS *mail_followup_to; + char *list_archive; + char *list_help; + char *list_owner; char *list_post; char *list_reply; + char *list_subscribe; + char *list_unsubscribe; char *subject; char *real_subj; /* offset of the real subject */ char *message_id; diff -r cd7be2bfff1c -r 19d38f8f81fb parse.c --- a/parse.c Fri Jan 01 05:19:48 2016 -0600 +++ b/parse.c Fri Jan 01 05:24:48 2016 -0600 @@ -1128,6 +1128,18 @@ matched = 1; } + else if (!ascii_strcasecmp (line + 1, "ist-Archive")) + { + matched = parse_list_header(&e->list_archive, p); + } + else if (!ascii_strcasecmp (line + 1, "ist-Help")) + { + matched = parse_list_header(&e->list_help, p); + } + else if (!ascii_strcasecmp (line + 1, "ist-Owner")) + { + matched = parse_list_header(&e->list_owner, p); + } else if (!ascii_strcasecmp (line + 1, "ist-Post")) { matched = parse_list_header(&e->list_post, p); @@ -1136,6 +1148,14 @@ { matched = parse_list_header(&e->list_reply, p); } + else if (!ascii_strcasecmp (line + 1, "ist-Subscribe")) + { + matched = parse_list_header(&e->list_subscribe, p); + } + else if (!ascii_strcasecmp (line + 1, "ist-Unsubscribe")) + { + matched = parse_list_header(&e->list_unsubscribe, p); + } break; case 'm': diff -r cd7be2bfff1c -r 19d38f8f81fb protos.h --- a/protos.h Fri Jan 01 05:19:48 2016 -0600 +++ b/protos.h Fri Jan 01 05:24:48 2016 -0600 @@ -211,6 +211,7 @@ void mutt_help (int); void mutt_draw_tree (CONTEXT *); void mutt_check_lookup_list (BODY *, char *, int); +void mutt_list_menu (CONTEXT *ctx, HEADER *cur); void mutt_make_attribution (CONTEXT *ctx, HEADER *cur, FILE *out); void mutt_make_forward_subject (ENVELOPE *env, CONTEXT *ctx, HEADER *cur); void mutt_make_help (char *, size_t, const char *, int, int);
