mutt stable branch with some hacks
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add ability to generate multipart/alternative using a filter script.

Create config variables $send_multipart_alternative (a quadoption)
and $send_multipart_alternative_filter. The filter script expects
output to be the mime type, a blank line, then the content.

Add ability to preview alternative from compose menu. <view-alt-text>
forces viewing as text. <view-alt-mailcap> forces using the mailcap.
Bind them to 'v' and 'V', respectively.

Improve <resend-message> so a multipart/alternative is stripped out,
which would just confuse the compose menu. Currently this preserves
the first alternative.

Allow alternative generation in batch mode, as long as the quadoption
is set to 'yes'.

+261 -10
+2
OPS
··· 49 49 OP_COMPOSE_TOGGLE_DISPOSITION "toggle disposition between inline/attachment" 50 50 OP_COMPOSE_TOGGLE_UNLINK "toggle whether to delete file after sending it" 51 51 OP_COMPOSE_UPDATE_ENCODING "update an attachment's encoding info" 52 + OP_COMPOSE_VIEW_ALT_TEXT "view multipart/alternative as text" 53 + OP_COMPOSE_VIEW_ALT_MAILCAP "view multipart/alternative using mailcap" 52 54 OP_COMPOSE_WRITE_MESSAGE "write the message to a folder" 53 55 OP_COPY_MESSAGE "copy a message to a file/mailbox" 54 56 OP_CREATE_ALIAS "create an alias from a message sender"
+20
compose.c
··· 1507 1507 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1508 1508 break; 1509 1509 1510 + case OP_COMPOSE_VIEW_ALT_TEXT: 1511 + case OP_COMPOSE_VIEW_ALT_MAILCAP: 1512 + { 1513 + BODY *alternative; 1514 + 1515 + if (!SendMultipartAltFilter) 1516 + { 1517 + mutt_error _("$send_multipart_alternative_filter is not set"); 1518 + break; 1519 + } 1520 + alternative = mutt_run_send_alternative_filter (msg->content); 1521 + if (!alternative) 1522 + break; 1523 + mutt_view_attachment (NULL, alternative, 1524 + op == OP_COMPOSE_VIEW_ALT_TEXT ? MUTT_AS_TEXT : MUTT_MAILCAP, 1525 + NULL, actx); 1526 + mutt_free_body (&alternative); 1527 + break; 1528 + } 1529 + 1510 1530 case OP_VIEW_ATTACH: 1511 1531 case OP_DISPLAY_HEADERS: 1512 1532 CHECK_COUNT;
+28
doc/manual.xml.head
··· 8135 8135 list, use the <command>unalternative_order</command> command. 8136 8136 </para> 8137 8137 8138 + <para> 8139 + Generating <literal>multipart/alternative</literal> content is supported 8140 + via the 8141 + <link linkend="send-multipart-alternative">$send_multipart_alternative</link> 8142 + quadoption and 8143 + <link linkend="send-multipart-alternative-filter">$send_multipart_alternative_filter</link> 8144 + filter script. The composed <literal>text/plain</literal> content 8145 + will be piped to the filter script's stdin. The output from the 8146 + filter script should be the generated mime/type of the content, a 8147 + blank line, and the content. For example: 8148 + </para> 8149 + <screen> 8150 + text/html 8151 + 8152 + &lt;html&gt; 8153 + &lt;body&gt; 8154 + Content in html format 8155 + &lt;/body&gt; 8156 + &lt;/html&gt; 8157 + </screen> 8158 + 8159 + <para> 8160 + A preview of the alternative can be viewed in the compose menu 8161 + using the functions <literal>&lt;view-alt-text&gt;</literal> and 8162 + <literal>&lt;view-alt-mailcap&gt;</literal>, bound to &quot;v&quot; 8163 + and &quot;V&quot; by default. 8164 + </para> 8165 + 8138 8166 </sect1> 8139 8167 8140 8168 <sect1 id="attachments">
+2
functions.h
··· 384 384 { "toggle-recode", OP_COMPOSE_TOGGLE_RECODE, NULL }, 385 385 { "update-encoding", OP_COMPOSE_UPDATE_ENCODING, "U" }, 386 386 { "view-attach", OP_VIEW_ATTACH, MUTT_ENTER_S }, 387 + { "view-alt-text", OP_COMPOSE_VIEW_ALT_TEXT, "v" }, 388 + { "view-alt-mailcap", OP_COMPOSE_VIEW_ALT_MAILCAP, "V" }, 387 389 { "send-message", OP_COMPOSE_SEND_MESSAGE, "y" }, 388 390 { "pipe-entry", OP_PIPE, "|" }, 389 391
+1
globals.h
··· 131 131 WHERE char *Realname; 132 132 WHERE short SearchContext; 133 133 WHERE char *SendCharset; 134 + WHERE char *SendMultipartAltFilter; 134 135 WHERE char *Sendmail; 135 136 WHERE char *Shell; 136 137 #ifdef USE_SIDEBAR
+21
init.h
··· 3114 3114 ** In case the text cannot be converted into one of these exactly, 3115 3115 ** mutt uses $$charset as a fallback. 3116 3116 */ 3117 + { "send_multipart_alternative", DT_QUAD, R_NONE, {.l=OPT_SENDMULTIPARTALT}, {.l=MUTT_NO} }, 3118 + /* 3119 + ** .pp 3120 + ** If \fIset\fP, Mutt will generate a multipart/alternative 3121 + ** container and an alternative part using the filter script specified in 3122 + ** $$send_multipart_alternative_filter. 3123 + ** See the section ``MIME Multipart/Alternative'' ($alternative-order). 3124 + ** .pp 3125 + ** Note that enabling multipart/alternative is not compatible with inline 3126 + ** PGP encryption. Mutt will prompt to use PGP/MIME in that case. 3127 + */ 3128 + { "send_multipart_alternative_filter", DT_PATH, R_NONE, {.p=&SendMultipartAltFilter}, {.p=0} }, 3129 + /* 3130 + ** .pp 3131 + ** This specifies a filter script, which will convert the main 3132 + ** (composed) message of the email to an alternative format. The 3133 + ** message will be piped to the filter's stdin. The expected output 3134 + ** of the filter is the generated mime type, e.g. text/html, 3135 + ** followed by a blank line, and then the converted content. 3136 + ** See the section ``MIME Multipart/Alternative'' ($alternative-order). 3137 + */ 3117 3138 { "sendmail", DT_PATH, R_NONE, {.p=&Sendmail}, {.p=SENDMAIL " -oem -oi"} }, 3118 3139 /* 3119 3140 ** .pp
+1
mutt.h
··· 331 331 OPT_QUIT, 332 332 OPT_REPLYTO, 333 333 OPT_RECALL, 334 + OPT_SENDMULTIPARTALT, 334 335 #if defined(USE_SSL) 335 336 OPT_SSLSTARTTLS, 336 337 #endif
+7 -7
postpone.c
··· 663 663 /* 664 664 * We don't need no primary multipart. 665 665 * Note: We _do_ preserve messages! 666 - * 667 - * XXX - we don't handle multipart/alternative in any 668 - * smart way when sending messages. However, one may 669 - * consider this a feature. 670 - * 671 666 */ 672 - 673 667 if (newhdr->content->type == TYPEMULTIPART) 674 668 newhdr->content = mutt_remove_multipart_mixed (newhdr->content); 675 669 676 - /* TODO: deal with multipart/alternative here */ 670 + /* Note: this just uses the *first* alternative and strips the rest. 671 + * It might be better to scan for text/plain. On the other hand, 672 + * mutt's alternative generation filter in theory allows composing 673 + * text/html and generating the text/plain from that. This way will 674 + * preserve the alternative originally composed by the user. 675 + */ 676 + newhdr->content = mutt_remove_multipart_alternative (newhdr->content); 677 677 678 678 s.fpin = bfp; 679 679
+3
protos.h
··· 91 91 ADDRESS *mutt_expand_aliases (ADDRESS *); 92 92 ADDRESS *mutt_parse_adrlist (ADDRESS *, const char *); 93 93 94 + BODY *mutt_run_send_alternative_filter (BODY *b); 94 95 BODY *mutt_make_file_attach (const char *); 95 96 BODY *mutt_make_message_attach (CONTEXT *, HEADER *, int); 96 97 BODY *mutt_remove_multipart (BODY *); 97 98 BODY *mutt_remove_multipart_mixed (BODY *); 98 99 BODY *mutt_make_multipart_mixed (BODY *); 100 + BODY *mutt_make_multipart_alternative (BODY *b, BODY *alternative); 101 + BODY *mutt_remove_multipart_alternative (BODY *b); 99 102 BODY *mutt_new_body (void); 100 103 BODY *mutt_parse_multipart (FILE *, const char *, LOFF_T, int); 101 104 BODY *mutt_parse_messageRFC822 (FILE *, BODY *);
+40 -3
send.c
··· 1098 1098 return (adr); 1099 1099 } 1100 1100 1101 + static int generate_multipart_alternative (HEADER *msg) 1102 + { 1103 + BODY *alternative; 1104 + 1105 + if (!SendMultipartAltFilter) 1106 + return 0; 1107 + if (query_quadoption (OPT_SENDMULTIPARTALT, 1108 + /* L10N: 1109 + This is the query for the $send_multipart_alternative quadoption. 1110 + Answering yes generates an alternative content using 1111 + $send_multipart_alternative_filter 1112 + */ 1113 + _("Generate multipart/alternative content?")) != MUTT_YES) 1114 + return 0; 1115 + 1116 + alternative = mutt_run_send_alternative_filter (msg->content); 1117 + if (!alternative) 1118 + return -1; 1119 + 1120 + msg->content = mutt_make_multipart_alternative (msg->content, alternative); 1121 + 1122 + return 0; 1123 + } 1124 + 1101 1125 static int send_message (HEADER *msg) 1102 1126 { 1103 1127 BUFFER *tempfile = NULL; ··· 1232 1256 && (mutt_strcmp (msg->content->subtype, "encrypted") == 0 || 1233 1257 mutt_strcmp (msg->content->subtype, "signed") == 0)) 1234 1258 { 1235 - if (clear_content->type == TYPEMULTIPART) 1259 + if ((clear_content->type == TYPEMULTIPART) && 1260 + !ascii_strcasecmp (clear_content->subtype, "mixed")) 1236 1261 { 1237 1262 if (!(msg->security & ENCRYPT) && (msg->security & SIGN)) 1238 1263 { ··· 1257 1282 save_content = msg->content; 1258 1283 } 1259 1284 } 1260 - else 1285 + else if (!ascii_strcasecmp (msg->content->subtype, "mixed")) 1261 1286 msg->content = msg->content->parts; 1262 1287 } 1263 1288 } ··· 2119 2144 } 2120 2145 } 2121 2146 2122 - /* multipart/alternative generation does here */ 2147 + if (!(flags & SENDBATCH) || 2148 + quadoption (OPT_SENDMULTIPARTALT) == MUTT_YES) 2149 + { 2150 + if (generate_multipart_alternative (msg)) 2151 + { 2152 + if (!(flags & SENDBATCH)) 2153 + goto main_loop; 2154 + else 2155 + goto cleanup; 2156 + } 2157 + } 2123 2158 2124 2159 if (msg->content->next) 2125 2160 msg->content = mutt_make_multipart_mixed (msg->content); ··· 2153 2188 mutt_protect (msg, pgpkeylist, 0) == -1) 2154 2189 { 2155 2190 msg->content = mutt_remove_multipart_mixed (msg->content); 2191 + msg->content = mutt_remove_multipart_alternative (msg->content); 2156 2192 2157 2193 FREE (&pgpkeylist); 2158 2194 ··· 2210 2246 FREE (&pgpkeylist); 2211 2247 mutt_free_envelope (&msg->content->mime_headers); /* protected headers */ 2212 2248 msg->content = mutt_remove_multipart_mixed (msg->content); 2249 + msg->content = mutt_remove_multipart_alternative (msg->content); 2213 2250 decode_descriptions (msg->content); 2214 2251 mutt_unprepare_envelope (msg->env); 2215 2252 goto main_loop;
+136
sendlib.c
··· 1391 1391 return (body); 1392 1392 } 1393 1393 1394 + BODY *mutt_run_send_alternative_filter (BODY *b) 1395 + { 1396 + BUFFER *alt_file = NULL; 1397 + FILE *b_fp = NULL, *alt_fp = NULL; 1398 + FILE *filter_in = NULL, *filter_out = NULL, *filter_err = NULL; 1399 + BODY *alternative = NULL; 1400 + pid_t thepid = 0; 1401 + char *mime = NULL; 1402 + char *buf = NULL; 1403 + size_t buflen; 1404 + 1405 + if (!SendMultipartAltFilter) 1406 + return NULL; 1407 + 1408 + if ((b_fp = safe_fopen (b->filename, "r")) == NULL) 1409 + { 1410 + mutt_perror (b->filename); 1411 + goto cleanup; 1412 + } 1413 + 1414 + alt_file = mutt_buffer_pool_get (); 1415 + mutt_buffer_mktemp (alt_file); 1416 + if ((alt_fp = safe_fopen (mutt_b2s (alt_file), "w")) == NULL) 1417 + { 1418 + mutt_perror (mutt_b2s (alt_file)); 1419 + goto cleanup; 1420 + } 1421 + 1422 + if ((thepid = mutt_create_filter (SendMultipartAltFilter, &filter_in, &filter_out, &filter_err)) < 0) 1423 + { 1424 + mutt_error (_("Error running \"%s\"!"), SendMultipartAltFilter); 1425 + goto cleanup; 1426 + } 1427 + 1428 + mutt_copy_stream (b_fp, filter_in); 1429 + safe_fclose (&b_fp); 1430 + safe_fclose (&filter_in); 1431 + 1432 + mime = mutt_read_line (NULL, &buflen, filter_out, NULL, 0); 1433 + if (!mime || !strchr (mime, '/')) 1434 + { 1435 + /* L10N: 1436 + The first line of output from $send_multipart_alternative_filter 1437 + should be a mime type, e.g. text/html. This error is generated 1438 + if that is missing. 1439 + */ 1440 + mutt_error (_("Missing mime type from output of \"%s\"!"), SendMultipartAltFilter); 1441 + goto cleanup; 1442 + } 1443 + 1444 + buf = mutt_read_line (NULL, &buflen, filter_out, NULL, 0); 1445 + if (!buf || mutt_strlen (buf)) 1446 + { 1447 + /* L10N: 1448 + The second line of output from $send_multipart_alternative_filter 1449 + should be a blank line. This error is generated if the blank line 1450 + is missing. 1451 + */ 1452 + mutt_error (_("Missing blank line separator from output of \"%s\"!"), SendMultipartAltFilter); 1453 + goto cleanup; 1454 + } 1455 + 1456 + mutt_copy_stream (filter_out, alt_fp); 1457 + safe_fclose (&filter_out); 1458 + safe_fclose (&filter_err); 1459 + 1460 + if (mutt_wait_filter (thepid) != 0) 1461 + { 1462 + mutt_error (_("Error running \"%s\"!"), SendMultipartAltFilter); 1463 + thepid = 0; 1464 + goto cleanup; 1465 + } 1466 + thepid = 0; 1467 + safe_fclose (&alt_fp); 1468 + 1469 + alternative = mutt_new_body (); 1470 + alternative->filename = safe_strdup (mutt_b2s (alt_file)); 1471 + alternative->unlink = 1; 1472 + alternative->use_disp = 0; 1473 + alternative->disposition = DISPINLINE; 1474 + 1475 + mutt_parse_content_type (mime, alternative); 1476 + mutt_update_encoding (alternative); 1477 + 1478 + cleanup: 1479 + safe_fclose (&b_fp); 1480 + if (alt_fp) 1481 + { 1482 + safe_fclose (&alt_fp); 1483 + mutt_unlink (mutt_b2s (alt_file)); 1484 + } 1485 + mutt_buffer_pool_release (&alt_file); 1486 + safe_fclose (&filter_in); 1487 + safe_fclose (&filter_out); 1488 + safe_fclose (&filter_err); 1489 + if (thepid > 0) 1490 + mutt_wait_filter (thepid); 1491 + FREE (&buf); 1492 + FREE (&mime); 1493 + 1494 + return alternative; 1495 + } 1496 + 1394 1497 static void run_mime_type_query (BODY *att) 1395 1498 { 1396 1499 FILE *fp, *fperr; ··· 1556 1659 if ((b->type == TYPEMULTIPART) && 1557 1660 !ascii_strcasecmp (b->subtype, "mixed")) 1558 1661 return mutt_remove_multipart (b); 1662 + 1663 + return b; 1664 + } 1665 + 1666 + BODY *mutt_make_multipart_alternative (BODY *b, BODY *alternative) 1667 + { 1668 + BODY *attachments, *mp; 1669 + 1670 + attachments = b->next; 1671 + 1672 + b->next = alternative; 1673 + mp = mutt_make_multipart (b, "alternative"); 1674 + 1675 + mp->next = attachments; 1676 + 1677 + return mp; 1678 + } 1679 + 1680 + BODY *mutt_remove_multipart_alternative (BODY *b) 1681 + { 1682 + BODY *attachments; 1683 + 1684 + if ((b->type != TYPEMULTIPART) || 1685 + ascii_strcasecmp (b->subtype, "alternative")) 1686 + return b; 1687 + 1688 + attachments = b->next; 1689 + b->next = NULL; 1690 + 1691 + b = mutt_remove_multipart (b); 1692 + 1693 + mutt_free_body (&b->next); 1694 + b->next = attachments; 1559 1695 1560 1696 return b; 1561 1697 }