Skip to content

Commit

Permalink
Initial support for multipart/alternative
Browse files Browse the repository at this point in the history
This is still WIP, mutt segfaults right after sending the email.

This is the work of @oblitium, based on this unattributed patch:
https://github.com/altercation/arch-packages/blob/master/mutt-dgc-hg/dgc.groupalts

References #587
  • Loading branch information
somini authored and flatcap committed Jan 2, 2018
1 parent 036950b commit 419b963
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 0 deletions.
162 changes: 162 additions & 0 deletions compose.c
Expand Up @@ -370,6 +370,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)
{
Expand Down Expand Up @@ -642,6 +644,41 @@ static void compose_menu_redraw(struct Menu *menu)
menu_redraw_current(menu);
}

/*
* compose_attach_swap: swap two adjacent entries in the attachment list.
*/
static void compose_attach_swap(struct Body *msg, struct AttachPtr **idx,
short first)
{
int i;
void *saved;
struct Body *part;

/* Reorder Body pointers.
* Must traverse msg from top since Body * has no previous ptr.
*/
for (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 */
saved = idx[first];
idx[first] = idx[first+1];
idx[first+1] = saved;

/* Swap ptr->num */
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
Expand Down Expand Up @@ -1025,6 +1062,131 @@ int mutt_compose_menu(struct Header *msg, /* structure for new message */
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:
{
struct Body *group, *bptr, *alts;
struct AttachPtr *gptr;
int j;
char *p;

if (menu->tagged < 2)
{
mutt_error(_("Grouping alternatives requires at least 2 tagged messages."));
break;
}

/* need to redo using mutt_gen_attach_list() */

group = mutt_mem_calloc(1, sizeof(struct Body));
group->type = TYPEMULTIPART;
group->subtype = "alternative";

alts = 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 = 0;

/* for first match, set group desc according to match */
# define ALTS_TAG "Alternatives for \"%s\""
if (!group->description)
{
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 (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;

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 = strdup("unknown alternative group");
}
menu->redraw = 1;
break;

case OP_COMPOSE_ATTACH_FILE:
{
char *prompt = NULL, **files = NULL;
Expand Down
12 changes: 12 additions & 0 deletions doc/manual.xml.head
Expand Up @@ -8139,6 +8139,18 @@ macro pager \cb |urlview\n
<para>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">
Expand Down
3 changes: 3 additions & 0 deletions functions.h
Expand Up @@ -422,6 +422,9 @@ const struct Binding OpCompose[] = { /* map: compose */
{ "edit-fcc", OP_COMPOSE_EDIT_FCC, "f" },
{ "filter-entry", OP_FILTER, "F" },
{ "get-attachment", OP_COMPOSE_GET_ATTACHMENT, "G" },
{ "group-alternatives", OP_COMPOSE_GROUP_ALTS, "&" },
{ "move-up", OP_COMPOSE_MOVE_UP, "-" },
{ "move-down", OP_COMPOSE_MOVE_DOWN, "+" },
{ "display-toggle-weed", OP_DISPLAY_HEADERS, "h" },
{ "ispell", OP_COMPOSE_ISPELL, "i" },
{ "print-entry", OP_PRINT, "l" },
Expand Down
3 changes: 3 additions & 0 deletions opcodes.h
Expand Up @@ -66,7 +66,10 @@
_fmt(OP_CREATE_MAILBOX, N_("create a new mailbox (IMAP only)")) \
_fmt(OP_EDIT_TYPE, N_("edit attachment content type")) \
_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_UP, N_("move an attachment up in the attachment list")) \
_fmt(OP_COMPOSE_MOVE_DOWN, N_("move an attachment down in the attachment list")) \
_fmt(OP_COMPOSE_NEW_MIME, N_("compose new attachment using mailcap entry")) \
_fmt(OP_COMPOSE_TOGGLE_RECODE, N_("toggle recoding of this attachment")) \
_fmt(OP_COMPOSE_POSTPONE_MESSAGE, N_("save this message to send later")) \
Expand Down

0 comments on commit 419b963

Please sign in to comment.