changeset: 6555:a4d885bb36ab user: Kevin McCarthy <ke...@8t8.us> date: Sun Feb 07 10:15:22 2016 -0800 link: http://dev.mutt.org/hg/mutt/rev/a4d885bb36ab
Add new flag -E to modify draft/include file. (closes #3799) Specifying -E with -i will cause mutt to directly edit the include file. Specifying -E with -H will cause the draft file to be regenerated from the latest version of the email on exit. Improve -H so that it will read (and write) multipart messages. changeset: 6556:87c46e1f6f8c user: Kevin McCarthy <ke...@8t8.us> date: Sun Feb 07 10:15:27 2016 -0800 link: http://dev.mutt.org/hg/mutt/rev/87c46e1f6f8c Fix pgp and smime decryption in mutt_prepare_template(). Change the "combined" multipart decryption block to only work for pgp, since mutt_is_multipart_encrypted() currently only checks for pgp headers and it therefore only worked for pgp in the first place. Fix the newhdr->security to be based on what that function returns, instead of the "context" hdr passed in. Add a smime decryption block below when iterating through the content. Fix the application/pgp decryption block to assign to hdr->security using the type of the app/pgp part instead of hdr->content. diffs (701 lines): diff -r e8f7a08cb7ac -r 87c46e1f6f8c compose.c --- a/compose.c Tue Jan 26 13:46:22 2016 -0800 +++ b/compose.c Sun Feb 07 10:15:27 2016 -0800 @@ -483,9 +483,10 @@ * -1 abort message */ int mutt_compose_menu (HEADER *msg, /* structure for new message */ - char *fcc, /* where to save a copy of the message */ - size_t fcclen, - HEADER *cur) /* current message */ + char *fcc, /* where to save a copy of the message */ + size_t fcclen, + HEADER *cur, /* current message */ + int flags) { char helpstr[LONG_STRING]; char buf[LONG_STRING]; @@ -1159,20 +1160,25 @@ case OP_EXIT: if ((i = query_quadoption (OPT_POSTPONE, _("Postpone this message?"))) == M_NO) { - while (idxlen-- > 0) - { - /* avoid freeing other attachments */ - idx[idxlen]->content->next = NULL; - idx[idxlen]->content->parts = NULL; - if (idx[idxlen]->unowned) - idx[idxlen]->content->unlink = 0; - mutt_free_body (&idx[idxlen]->content); - FREE (&idx[idxlen]->tree); - FREE (&idx[idxlen]); - } - FREE (&idx); - idxlen = 0; - idxmax = 0; + for (i = 0; i < idxlen; i++) + if (idx[i]->unowned) + idx[i]->content->unlink = 0; + + if (!(flags & M_COMPOSE_NOFREEHEADER)) + { + while (idxlen-- > 0) + { + /* avoid freeing other attachments */ + idx[idxlen]->content->next = NULL; + idx[idxlen]->content->parts = NULL; + mutt_free_body (&idx[idxlen]->content); + FREE (&idx[idxlen]->tree); + FREE (&idx[idxlen]); + } + FREE (&idx); + idxlen = 0; + idxmax = 0; + } r = -1; loop = 0; break; diff -r e8f7a08cb7ac -r 87c46e1f6f8c doc/manual.xml.head --- a/doc/manual.xml.head Tue Jan 26 13:46:22 2016 -0800 +++ b/doc/manual.xml.head Sun Feb 07 10:15:27 2016 -0800 @@ -8304,6 +8304,7 @@ <row><entry>-b</entry><entry>specify a blind carbon-copy (BCC) address</entry></row> <row><entry>-c</entry><entry>specify a carbon-copy (Cc) address</entry></row> <row><entry>-D</entry><entry>print the value of all Mutt variables to stdout</entry></row> +<row><entry>-E</entry><entry>edit the draft (-H) or include (-i) file</entry></row> <row><entry>-e</entry><entry>specify a config command to be run after initialization files are read</entry></row> <row><entry>-f</entry><entry>specify a mailbox to load</entry></row> <row><entry>-F</entry><entry>specify an alternate file to read initialization commands</entry></row> @@ -8349,14 +8350,14 @@ <cmdsynopsis> <command>mutt</command> -<arg choice="opt"><option>-n</option></arg> +<arg choice="opt"><option>-En</option></arg> <arg choice="opt"><option>-F</option> <replaceable>muttrc</replaceable> </arg> <arg choice="opt"><option>-c</option> <replaceable>address</replaceable> </arg> -<arg choice="opt"><option>-i</option> +<arg choice="opt"><option>-Hi</option> <replaceable>filename</replaceable> </arg> <arg choice="opt"><option>-s</option> @@ -8395,6 +8396,24 @@ </para> <para> +An include file passed with <literal>-i</literal> will be used as the +body of the message. When combined with <literal>-E</literal>, the +include file will be directly edited during message composition. The +file will be modified regardless of whether the message is sent or +aborted. +</para> + +<para> +A draft file passed with <literal>-H</literal> will be used as the +initial header and body for the message. Multipart messages can be +used as a draft file. When combined with <literal>-E</literal>, the +draft file will be updated to the final state of the message after +composition, regardless of whether the message is sent, aborted, or +even postponed. Note that if the message is sent encrypted or signed, +the draft file will be saved that way too. +</para> + +<para> All files passed with <literal>-a</literal> <emphasis>file</emphasis> will be attached as a MIME part to the message. To attach a single or several files, use <quote>--</quote> to separate files and recipient diff -r e8f7a08cb7ac -r 87c46e1f6f8c doc/mutt.man --- a/doc/mutt.man Tue Jan 26 13:46:22 2016 -0800 +++ b/doc/mutt.man Sun Feb 07 10:15:27 2016 -0800 @@ -1,7 +1,7 @@ .\" -*-nroff-*- .\" .\" -.\" Copyright (C) 1996-2004 Michael R. Elkins <m...@cs.hmc.edu> +.\" Copyright (C) 1996-2016 Michael R. Elkins <m...@cs.hmc.edu> .\" .\" 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 @@ -27,7 +27,7 @@ [\-e \fIcmd\fP] [\-F \fIfile\fP] [\-m \fItype\fP] [\-f \fIfile\fP] .PP .B mutt -[\-nx] +[\-Enx] [\-e \fIcmd\fP] [\-F \fIfile\fP] [\-H \fIfile\fP] @@ -94,6 +94,9 @@ recommended. .IP "-D" Print the value of all configuration options to stdout. +.IP "-E" +Causes the draft file specified by -H or include file specified by -i +to be edited during message composition. .IP "-e \fIcommand\fP" Specify a configuration command to be run after processing of initialization files. diff -r e8f7a08cb7ac -r 87c46e1f6f8c main.c --- a/main.c Tue Jan 26 13:46:22 2016 -0800 +++ b/main.c Sun Feb 07 10:15:27 2016 -0800 @@ -114,7 +114,7 @@ puts _( "usage: mutt [<options>] [-z] [-f <file> | -yZ]\n\ - mutt [<options>] [-x] [-Hi <file>] [-s <subj>] [-bc <addr>] [-a <file> [...] --] <addr> [...]\n\ + mutt [<options>] [-Ex] [-Hi <file>] [-s <subj>] [-bc <addr>] [-a <file> [...] --] <addr> [...]\n\ mutt [<options>] [-x] [-s <subj>] [-bc <addr>] [-a <file> [...] --] <addr> [...] < message\n\ mutt [<options>] -p\n\ mutt [<options>] -A <alias> [...]\n\ @@ -134,7 +134,8 @@ puts _(" -d <level>\tlog debugging output to ~/.muttdebug0"); #endif puts _( -" -e <command>\tspecify a command to be executed after initialization\n\ +" -E\t\tedit the draft (-H) or include (-i) file\n\ + -e <command>\tspecify a command to be executed after initialization\n\ -f <file>\tspecify which mailbox to read\n\ -F <file>\tspecify an alternate muttrc file\n\ -H <file>\tspecify a draft file to read header and body from\n\ @@ -572,6 +573,7 @@ int i; int explicit_folder = 0; int dump_variables = 0; + int edit_infile = 0; extern char *optarg; extern int optind; int double_dash = argc, nargc = 1; @@ -625,7 +627,7 @@ argv[nargc++] = argv[optind]; } - if ((i = getopt (argc, argv, "+A:a:b:F:f:c:Dd:e:H:s:i:hm:npQ:RvxyzZ")) != EOF) + if ((i = getopt (argc, argv, "+A:a:b:F:f:c:Dd:Ee:H:s:i:hm:npQ:RvxyzZ")) != EOF) switch (i) { case 'A': @@ -673,6 +675,10 @@ #endif break; + case 'E': + edit_infile = 1; + break; + case 'e': commands = mutt_add_list (commands, optarg); break; @@ -859,10 +865,12 @@ optind < argc) { FILE *fin = NULL; + FILE *fout = NULL; char buf[LONG_STRING]; char *tempfile = NULL, *infile = NULL; - char *bodytext = NULL; + char *bodytext = NULL, *bodyfile = NULL; int rv = 0; + char expanded_infile[_POSIX_PATH_MAX]; if (!option (OPTNOCURSES)) mutt_flushinp (); @@ -900,81 +908,141 @@ msg->env->subject = safe_strdup (subject); if (draftFile) + { infile = draftFile; + includeFile = NULL; + } else if (includeFile) infile = includeFile; + else + edit_infile = 0; if (infile || bodytext) { + /* Prepare fin and expanded_infile. */ if (infile) { if (mutt_strcmp ("-", infile) == 0) + { + if (edit_infile) + { + fputs (_("Cannot use -E flag with stdin\n"), stderr); + exit (1); + } fin = stdin; - else + } + else { - char path[_POSIX_PATH_MAX]; - - strfcpy (path, infile, sizeof (path)); - mutt_expand_path (path, sizeof (path)); - if ((fin = fopen (path, "r")) == NULL) + strfcpy (expanded_infile, infile, sizeof (expanded_infile)); + mutt_expand_path (expanded_infile, sizeof (expanded_infile)); + if ((fin = fopen (expanded_infile, "r")) == NULL) { if (!option (OPTNOCURSES)) mutt_endwin (NULL); - perror (path); + perror (expanded_infile); exit (1); } } + } - if (draftFile) + /* Copy input to a tempfile, and re-point fin to the tempfile. + * Note: stdin is always copied to a tempfile, ensuring draftFile + * can stat and get the correct st_size below. + */ + if (!edit_infile) + { + mutt_mktemp (buf, sizeof (buf)); + tempfile = safe_strdup (buf); + + if ((fout = safe_fopen (tempfile, "w")) == NULL) { - ENVELOPE *opts_env = msg->env; - msg->env = mutt_read_rfc822_header (fin, NULL, 1, 0); + if (!option (OPTNOCURSES)) + mutt_endwin (NULL); + perror (tempfile); + safe_fclose (&fin); + FREE (&tempfile); + exit (1); + } + if (fin) + { + mutt_copy_stream (fin, fout); + if (fin != stdin) + safe_fclose (&fin); + } + else if (bodytext) + fputs (bodytext, fout); + safe_fclose (&fout); - rfc822_append (&msg->env->to, opts_env->to, 0); - rfc822_append (&msg->env->cc, opts_env->cc, 0); - rfc822_append (&msg->env->bcc, opts_env->bcc, 0); - if (opts_env->subject) - mutt_str_replace (&msg->env->subject, opts_env->subject); - - mutt_free_envelope (&opts_env); + if ((fin = fopen (tempfile, "r")) == NULL) + { + if (!option (OPTNOCURSES)) + mutt_endwin (NULL); + perror (tempfile); + FREE (&tempfile); + exit (1); } } + /* If editing the infile, keep it around afterwards so + * it doesn't get unlinked, and we can rebuild the draftFile + */ + else + sendflags |= SENDNOFREEHEADER; - mutt_mktemp (buf, sizeof (buf)); - tempfile = safe_strdup (buf); + /* Parse the draftFile into the full HEADER/BODY structure. + * Set SENDUSEHDRBODY so ci_send_message doesn't overwrite + * our msg->content. + */ + if (draftFile) + { + HEADER *context_hdr = NULL; + ENVELOPE *opts_env = msg->env; + struct stat st; - /* is the following if still needed? */ - - if (tempfile) - { - FILE *fout; + sendflags |= SENDUSEHDRBODY; - if ((fout = safe_fopen (tempfile, "w")) == NULL) - { - if (!option (OPTNOCURSES)) - mutt_endwin (NULL); - perror (tempfile); - safe_fclose (&fin); - FREE (&tempfile); - exit (1); - } - if (fin) - mutt_copy_stream (fin, fout); - else if (bodytext) - fputs (bodytext, fout); - safe_fclose (&fout); + /* Set up a "context" header with just enough information so that + * mutt_prepare_template() can parse the message in fin. + */ + context_hdr = mutt_new_header (); + context_hdr->offset = 0; + context_hdr->content = mutt_new_body (); + fstat (fileno (fin), &st); + context_hdr->content->length = st.st_size; + + mutt_prepare_template (fin, NULL, msg, context_hdr, 0); + + rfc822_append (&msg->env->to, opts_env->to, 0); + rfc822_append (&msg->env->cc, opts_env->cc, 0); + rfc822_append (&msg->env->bcc, opts_env->bcc, 0); + if (opts_env->subject) + mutt_str_replace (&msg->env->subject, opts_env->subject); + + mutt_free_envelope (&opts_env); + mutt_free_header (&context_hdr); } + /* Editing the includeFile: pass it directly in. + * Note that SENDNOFREEHEADER is set above so it isn't unlinked. + */ + else if (edit_infile) + bodyfile = expanded_infile; + /* For bodytext and unedited includeFile: use the tempfile. + */ + else + bodyfile = tempfile; - if (fin && fin != stdin) + if (fin) safe_fclose (&fin); } FREE (&bodytext); - + if (attach) { LIST *t = attach; - BODY *a = NULL; + BODY *a = msg->content; + + while (a && a->next) + a = a->next; while (t) { @@ -998,7 +1066,62 @@ mutt_free_list (&attach); } - rv = ci_send_message (sendflags, msg, tempfile, NULL, NULL); + rv = ci_send_message (sendflags, msg, bodyfile, NULL, NULL); + + if (edit_infile) + { + if (includeFile) + msg->content->unlink = 0; + else if (draftFile) + { + if (truncate (expanded_infile, 0) == -1) + { + if (!option (OPTNOCURSES)) + mutt_endwin (NULL); + perror (expanded_infile); + exit (1); + } + if ((fout = safe_fopen (expanded_infile, "a")) == NULL) + { + if (!option (OPTNOCURSES)) + mutt_endwin (NULL); + perror (expanded_infile); + exit (1); + } + + /* If the message was sent or postponed, these will already + * have been done. + */ + if (rv < 0) + { + if (msg->content->next) + msg->content = mutt_make_multipart (msg->content); + mutt_encode_descriptions (msg->content, 1); + mutt_prepare_envelope (msg->env, 0); + mutt_env_to_intl (msg->env, NULL, NULL); + } + + mutt_write_rfc822_header (fout, msg->env, msg->content, -1, 0); + fputc ('\n', fout); + if ((mutt_write_mime_body (msg->content, fout) == -1)) + { + if (!option (OPTNOCURSES)) + mutt_endwin (NULL); + safe_fclose (&fout); + exit (1); + } + safe_fclose (&fout); + } + + mutt_free_header (&msg); + } + + /* !edit_infile && draftFile will leave the tempfile around */ + if (tempfile) + { + unlink (tempfile); + FREE (&tempfile); + } if (!option (OPTNOCURSES)) mutt_endwin (NULL); diff -r e8f7a08cb7ac -r 87c46e1f6f8c mutt.h --- a/mutt.h Tue Jan 26 13:46:22 2016 -0800 +++ b/mutt.h Sun Feb 07 10:15:27 2016 -0800 @@ -296,6 +296,11 @@ #define SENDKEY (1<<7) #define SENDRESEND (1<<8) #define SENDPOSTPONEDFCC (1<<9) /* used by mutt_get_postponed() to signal that the x-mutt-fcc header field was present */ +#define SENDNOFREEHEADER (1<<10) /* Used by the -E flag */ +#define SENDUSEHDRBODY (1<<11) /* Used by the -H flag */ + +/* flags for mutt_compose_menu() */ +#define M_COMPOSE_NOFREEHEADER (1<<0) /* flags to _mutt_select_file() */ #define M_SEL_BUFFY (1<<0) diff -r e8f7a08cb7ac -r 87c46e1f6f8c postpone.c --- a/postpone.c Tue Jan 26 13:46:22 2016 -0800 +++ b/postpone.c Sun Feb 07 10:15:27 2016 -0800 @@ -541,9 +541,9 @@ char file[_POSIX_PATH_MAX]; BODY *b; FILE *bfp; - int rv = -1; STATE s; + int sec_type; memset (&s, 0, sizeof (s)); @@ -574,17 +574,15 @@ /* decrypt pgp/mime encoded messages */ - if ((WithCrypto & (APPLICATION_PGP|APPLICATION_SMIME) & hdr->security) - && mutt_is_multipart_encrypted (newhdr->content)) + if ((WithCrypto & APPLICATION_PGP) && + (sec_type = mutt_is_multipart_encrypted (newhdr->content))) { - int ccap = WithCrypto & (APPLICATION_PGP|APPLICATION_SMIME) & hdr->security; - newhdr->security |= ENCRYPT | ccap; - if (!crypt_valid_passphrase (ccap)) + newhdr->security |= sec_type; + if (!crypt_valid_passphrase (sec_type)) goto err; mutt_message _("Decrypting message..."); - if (((ccap & APPLICATION_PGP) && crypt_pgp_decrypt_mime (fp, &bfp, newhdr->content, &b) == -1) - || ((ccap & APPLICATION_SMIME) && crypt_smime_decrypt_mime (fp, &bfp, newhdr->content, &b) == -1) + if ((crypt_pgp_decrypt_mime (fp, &bfp, newhdr->content, &b) == -1) || b == NULL) { err: @@ -678,18 +676,26 @@ goto bail; - if ((WithCrypto & APPLICATION_PGP) - && (mutt_is_application_pgp (b) & (ENCRYPT|SIGN))) + if ((WithCrypto & APPLICATION_PGP) && + ((sec_type = mutt_is_application_pgp (b)) & (ENCRYPT|SIGN))) { - mutt_body_handler (b, &s); - newhdr->security |= mutt_is_application_pgp (newhdr->content); + newhdr->security |= sec_type; b->type = TYPETEXT; mutt_str_replace (&b->subtype, "plain"); mutt_delete_parameter ("x-action", &b->parameter); } + else if ((WithCrypto & APPLICATION_SMIME) && + ((sec_type = mutt_is_application_smime (b)) & (ENCRYPT|SIGN))) + { + mutt_body_handler (b, &s); + + newhdr->security |= sec_type; + b->type = TYPETEXT; + mutt_str_replace (&b->subtype, "plain"); + } else mutt_decode_attachment (b, &s); diff -r e8f7a08cb7ac -r 87c46e1f6f8c protos.h --- a/protos.h Tue Jan 26 13:46:22 2016 -0800 +++ b/protos.h Sun Feb 07 10:15:27 2016 -0800 @@ -186,6 +186,7 @@ int mutt_filter_unprintable (char **); void mutt_curses_error (const char *, ...); void mutt_curses_message (const char *, ...); +void mutt_encode_descriptions (BODY *, short); void mutt_encode_path (char *, size_t, const char *); void mutt_enter_command (void); void mutt_expand_aliases_env (ENVELOPE *); @@ -358,7 +359,7 @@ int mutt_wstr_trunc (const char *, size_t, size_t, size_t *); int mutt_charlen (const char *s, int *); int mutt_strwidth (const char *); -int mutt_compose_menu (HEADER *, char *, size_t, HEADER *); +int mutt_compose_menu (HEADER *, char *, size_t, HEADER *, int); int mutt_thread_set_flag (HEADER *, int, int, int); int mutt_user_is_recipient (HEADER *); void mutt_update_num_postponed (void); diff -r e8f7a08cb7ac -r 87c46e1f6f8c send.c --- a/send.c Tue Jan 26 13:46:22 2016 -0800 +++ b/send.c Sun Feb 07 10:15:27 2016 -0800 @@ -1042,7 +1042,7 @@ } /* rfc2047 encode the content-descriptions */ -static void encode_descriptions (BODY *b, short recurse) +void mutt_encode_descriptions (BODY *b, short recurse) { BODY *t; @@ -1053,7 +1053,7 @@ rfc2047_encode_string (&t->description); } if (recurse && t->parts) - encode_descriptions (t->parts, recurse); + mutt_encode_descriptions (t->parts, recurse); } } @@ -1136,6 +1136,11 @@ return c; } +/* + * Returns 0 if the message was successfully sent + * -1 if the message was aborted or an error occurred + * 1 if the message was postponed + */ int ci_send_message (int flags, /* send mode */ HEADER *msg, /* template to use for new message */ @@ -1224,29 +1229,37 @@ if (! (flags & (SENDKEY | SENDPOSTPONED | SENDRESEND))) { - pbody = mutt_new_body (); - pbody->next = msg->content; /* don't kill command-line attachments */ - msg->content = pbody; + /* When SENDUSEHDRBODY is set, the caller has already + * created the "parent" body structure. + */ + if (! (flags & SENDUSEHDRBODY)) + { + pbody = mutt_new_body (); + pbody->next = msg->content; /* don't kill command-line attachments */ + msg->content = pbody; - if (!(ctype = safe_strdup (ContentType))) - ctype = safe_strdup ("text/plain"); - mutt_parse_content_type (ctype, msg->content); - FREE (&ctype); - msg->content->unlink = 1; - msg->content->use_disp = 0; - msg->content->disposition = DISPINLINE; - - if (!tempfile) - { - mutt_mktemp (buffer, sizeof (buffer)); - tempfp = safe_fopen (buffer, "w+"); - msg->content->filename = safe_strdup (buffer); + if (!(ctype = safe_strdup (ContentType))) + ctype = safe_strdup ("text/plain"); + mutt_parse_content_type (ctype, msg->content); + FREE (&ctype); + msg->content->unlink = 1; + msg->content->use_disp = 0; + msg->content->disposition = DISPINLINE; + + if (!tempfile) + { + mutt_mktemp (buffer, sizeof (buffer)); + tempfp = safe_fopen (buffer, "w+"); + msg->content->filename = safe_strdup (buffer); + } + else + { + tempfp = safe_fopen (tempfile, "a+"); + msg->content->filename = safe_strdup (tempfile); + } } else - { - tempfp = safe_fopen (tempfile, "a+"); - msg->content->filename = safe_strdup (tempfile); - } + tempfp = safe_fopen (msg->content->filename, "a+"); if (!tempfp) { @@ -1586,7 +1599,8 @@ fcc_error = 0; /* reset value since we may have failed before */ mutt_pretty_mailbox (fcc, sizeof (fcc)); - i = mutt_compose_menu (msg, fcc, sizeof (fcc), cur); + i = mutt_compose_menu (msg, fcc, sizeof (fcc), cur, + (flags & SENDNOFREEHEADER ? M_COMPOSE_NOFREEHEADER : 0)); if (i == -1) { /* abort */ @@ -1627,7 +1641,7 @@ */ msg->read = 0; msg->old = 0; - encode_descriptions (msg->content, 1); + mutt_encode_descriptions (msg->content, 1); mutt_prepare_envelope (msg->env, 0); mutt_env_to_intl (msg->env, NULL, NULL); /* Handle bad IDNAs the next time. */ @@ -1640,6 +1654,7 @@ } mutt_update_num_postponed (); mutt_message _("Message postponed."); + rv = 1; goto cleanup; } } @@ -1687,7 +1702,7 @@ * in case of error. Ugh. */ - encode_descriptions (msg->content, 1); + mutt_encode_descriptions (msg->content, 1); /* * Make sure that clear_content and free_clear_content are @@ -1716,7 +1731,7 @@ decode_descriptions (msg->content); goto main_loop; } - encode_descriptions (msg->content, 0); + mutt_encode_descriptions (msg->content, 0); } /* @@ -1926,7 +1941,8 @@ } safe_fclose (&tempfp); - mutt_free_header (&msg); + if (! (flags & SENDNOFREEHEADER)) + mutt_free_header (&msg); return rv; }