# 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);

Reply via email to