]> granicus.if.org Git - neomutt/commitdiff
Initial support for multipart/alternative
authorsomini <dev@somini.xyz>
Sat, 26 Aug 2017 19:26:42 +0000 (20:26 +0100)
committerRichard Russon <rich@flatcap.org>
Sat, 26 Aug 2017 19:26:42 +0000 (20:26 +0100)
This is still WIP, mutt segfaults right after sending the email.

This is the work of @oblitum.

References #587

compose.c
doc/manual.xml.head
functions.h
opcodes.h

index c7869dbd02e3b527e7a1c023676ae9dc6ea78fb3..9640a0eb171bb78c54e670277bd30527e689c047 100644 (file)
--- a/compose.c
+++ b/compose.c
@@ -367,6 +367,8 @@ static int check_attachments(struct AttachCtx *actx)
 
   for (int i = 0; i < actx->idxlen; i++)
   {
+    if (actx->idx[i]->content->type == TYPEMULTIPART)
+      continue;
     mutt_str_strfcpy(pretty, actx->idx[i]->content->filename, sizeof(pretty));
     if (stat(actx->idx[i]->content->filename, &st) != 0)
     {
@@ -639,6 +641,39 @@ static void compose_menu_redraw(struct Menu *menu)
     menu_redraw_current(menu);
 }
 
+/**
+ * compose_attach_swap - Swap two adjacent entries in the attachment list
+ * @param msg   Body of email
+ * @param idx   Array of Attachments
+ * @param first Index of first attachment to swap
+ */
+static void compose_attach_swap(struct Body *msg, struct AttachPtr **idx, short first)
+{
+  /* Reorder Body pointers.
+   * Must traverse msg from top since Body has no previous ptr.
+   */
+  for (struct Body *part = msg; part; part = part->next)
+  {
+    if (part->next == idx[first]->content)
+    {
+      idx[first]->content->next = idx[first + 1]->content->next;
+      idx[first + 1]->content->next = idx[first]->content;
+      part->next = idx[first + 1]->content;
+      break;
+    }
+  }
+
+  /* Reorder index */
+  struct AttachPtr *saved = idx[first];
+  idx[first] = idx[first + 1];
+  idx[first + 1] = saved;
+
+  /* Swap ptr->num */
+  int i = idx[first]->num;
+  idx[first]->num = idx[first + 1]->num;
+  idx[first + 1]->num = i;
+}
+
 /**
  * cum_attachs_size - Cumulative Attachments Size
  * @param menu Menu listing attachments
@@ -1023,6 +1058,129 @@ int mutt_compose_menu(struct Header *msg, char *fcc, size_t fcclen,
         mutt_message_hook(NULL, msg, MUTT_SEND2HOOK);
         break;
 
+      case OP_COMPOSE_MOVE_UP:
+        if (menu->current == 0)
+        {
+          mutt_error(_("Attachment is already at top."));
+          break;
+        }
+        if (menu->current == 1)
+        {
+          mutt_error(_("The fundamental part cannot be moved."));
+          break;
+        }
+        compose_attach_swap(msg->content, actx->idx, menu->current - 1);
+        menu->redraw = 1;
+        menu->current--;
+        break;
+
+      case OP_COMPOSE_MOVE_DOWN:
+        if (menu->current == actx->idxlen - 1)
+        {
+          mutt_error(_("Attachment is already at bottom."));
+          break;
+        }
+        if (menu->current == 0)
+        {
+          mutt_error(_("The fundamental part cannot be moved."));
+          break;
+        }
+        compose_attach_swap(msg->content, actx->idx, menu->current);
+        menu->redraw = 1;
+        menu->current++;
+        break;
+
+      case OP_COMPOSE_GROUP_ALTS:
+      {
+        if (menu->tagged < 2)
+        {
+          mutt_error(
+              _("Grouping alternatives requires at least 2 tagged messages."));
+          break;
+        }
+
+        /* need to redo using mutt_gen_attach_list() */
+
+        struct Body *group = mutt_body_new();
+        group->type = TYPEMULTIPART;
+        group->subtype = "alternative";
+
+        struct Body *alts = NULL;
+        struct Body *bptr = NULL;
+        for (i = 0, bptr = msg->content; bptr && bptr->next;)
+        {
+          /* always look at bptr->next, not bptr itself */
+          if (bptr->next->tagged)
+          {
+            /* untag */
+            bptr->next->tagged = false;
+
+            /* for first match, set group desc according to match */
+#define ALTS_TAG "Alternatives for \"%s\""
+            if (!group->description)
+            {
+              char *p = bptr->next->description;
+              if (!p)
+                p = bptr->next->filename;
+              if (p)
+              {
+                group->description =
+                    mutt_mem_calloc(1, strlen(p) + strlen(ALTS_TAG) + 1);
+                sprintf(group->description, ALTS_TAG, p);
+              }
+            }
+
+            /* append bptr->next to the alts list,
+             * and remove from the msg->content list */
+            if (alts == NULL)
+            {
+              group->parts = alts = bptr->next;
+              bptr->next = bptr->next->next;
+              alts->next = NULL;
+            }
+            else
+            {
+              alts->next = bptr->next;
+              bptr->next = bptr->next->next;
+              alts = alts->next;
+              alts->next = NULL;
+            }
+
+            /* now delink the idx entry */
+            for (int j = i + 1; j < actx->idxlen - 1; ++j)
+            {
+              actx->idx[j] = actx->idx[j + 1];
+            }
+            actx->idxlen--;
+          }
+          else
+          {
+            bptr = bptr->next;
+            i++;
+          }
+        }
+
+        /* add group to attachment list */
+        for (bptr = msg->content; bptr->next; bptr = bptr->next)
+          ;
+        bptr->next = group;
+        group->next = NULL;
+
+        struct AttachPtr *gptr = mutt_mem_calloc(1, sizeof(struct AttachPtr));
+        gptr->content = group;
+        actx->idx[actx->idxlen] = gptr;
+        update_idx(menu, actx, gptr);
+
+        /* add a boundary */
+        mutt_generate_boundary(&group->parameter);
+
+        /* if no group desc yet, make one up */
+        if (!group->description)
+          group->description = mutt_str_strdup("unknown alternative group");
+      }
+        menu->redraw = 1;
+        break;
+
       case OP_COMPOSE_ATTACH_FILE:
       {
         char *prompt = _("Attach file");
index 90cc01f2eaab8f8f8aa984a7c015bcf1a2fadb84..987816cc8598325b671259730c7d9e0427773c38 100644 (file)
@@ -9079,6 +9079,22 @@ echo "Sourcing muttrc file" unset confirmappend macro index ,a "&lt;save-message
           See table <xref linkend="tab-attachment-bindings" /> for all
           available functions.
         </para>
+        <para>
+          Mutt includes some primitive ability to compose multipart/alternative
+          parts. In the Compose menu, attach the two (or more) alternatives as
+          usual.  For example, attach "invitation.html" and then
+          "invitation.txt". (You can reorder them using the &lt;move-up&gt; (-)
+          and &lt;move-down&gt; (+) bindings.) Edit the descriptions, if you
+          wish. Then tag the attachments that are alternatives, and press the
+          &lt;group-alternatives&gt; (&amp;) binding to group them together. The
+          separate parts will be replaced by a single new part with the
+          multipart/alternative type. From this point on, the alternatives must
+          be manipulated or deleted as a group.
+        </para>
+        <para>
+          Beware that such messages cannot be postponed. Once two attachments
+          are grouped as alternatives, they must be sent or lost.
+        </para>
       </sect2>
 
       <sect2 id="compose-menu">
index 2a91c4ba73e63e0bc0d9ca5f9a65528e0f23d713..17e590000731beab5e103d68e56473dde4cb87e2 100644 (file)
@@ -451,10 +451,13 @@ const struct Binding OpCompose[] = { /* map: compose */
   { "filter-entry",          OP_FILTER,                      "F" },
   { "forget-passphrase",     OP_FORGET_PASSPHRASE,           "\006" },
   { "get-attachment",        OP_COMPOSE_GET_ATTACHMENT,      "G" },
+  { "group-alternatives",    OP_COMPOSE_GROUP_ALTS,          "&" },
   { "ispell",                OP_COMPOSE_ISPELL,              "i" },
 #ifdef MIXMASTER
   { "mix",                   OP_COMPOSE_MIX,                 "M" },
 #endif
+  { "move-down",             OP_COMPOSE_MOVE_DOWN,           "+" },
+  { "move-up",               OP_COMPOSE_MOVE_UP,             "-" },
   { "new-mime",              OP_COMPOSE_NEW_MIME,            "n" },
   { "pgp-menu",              OP_COMPOSE_PGP_MENU,            "p" },
   { "pipe-entry",            OP_PIPE,                        "|" },
index 3240a0ca93c2b647fcd4dbfe26e8cbf0b306e12f..5ca79bb79a20f2582d747ea4592f54cdd43ccb10 100644 (file)
--- a/opcodes.h
+++ b/opcodes.h
   _fmt(OP_COMPOSE_EDIT_TO,                N_("edit the TO list")) \
   _fmt(OP_COMPOSE_EDIT_X_COMMENT_TO,      N_("edit the X-Comment-To field")) \
   _fmt(OP_COMPOSE_GET_ATTACHMENT,         N_("get a temporary copy of an attachment")) \
+  _fmt(OP_COMPOSE_GROUP_ALTS,             N_("group tagged attachments as multipart/alternative")) \
   _fmt(OP_COMPOSE_ISPELL,                 N_("run ispell on the message")) \
+  _fmt(OP_COMPOSE_MOVE_DOWN,              N_("move an attachment down in the attachment list")) \
+  _fmt(OP_COMPOSE_MOVE_UP,                N_("move an attachment up in the attachment list")) \
   _fmt(OP_COMPOSE_NEW_MIME,               N_("compose new attachment using mailcap entry")) \
   _fmt(OP_COMPOSE_POSTPONE_MESSAGE,       N_("save this message to send later")) \
   _fmt(OP_COMPOSE_RENAME_ATTACHMENT,      N_("send attachment with a different name")) \