]> granicus.if.org Git - neomutt/blob - send.c
merge: light refactoring
[neomutt] / send.c
1 /**
2  * @file
3  * Prepare and send an email
4  *
5  * @authors
6  * Copyright (C) 1996-2002,2004,2010,2012-2013 Michael R. Elkins <me@mutt.org>
7  * Copyright (C) 2019 Pietro Cerutti <gahr@gahr.ch>
8  *
9  * @copyright
10  * This program is free software: you can redistribute it and/or modify it under
11  * the terms of the GNU General Public License as published by the Free Software
12  * Foundation, either version 2 of the License, or (at your option) any later
13  * version.
14  *
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License along with
21  * this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23
24 /**
25  * @page send Prepare and send an email
26  *
27  * Prepare and send an email
28  */
29
30 #include "config.h"
31 #include <errno.h>
32 #include <limits.h>
33 #include <locale.h>
34 #include <stdbool.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <sys/stat.h>
38 #include <time.h>
39 #include <unistd.h>
40 #include "mutt/mutt.h"
41 #include "address/lib.h"
42 #include "config/lib.h"
43 #include "email/lib.h"
44 #include "core/lib.h"
45 #include "mutt.h"
46 #include "send.h"
47 #include "alias.h"
48 #include "compose.h"
49 #include "context.h"
50 #include "copy.h"
51 #include "curs_lib.h"
52 #include "edit.h"
53 #include "filter.h"
54 #include "globals.h"
55 #include "handler.h"
56 #include "hdrline.h"
57 #include "hook.h"
58 #include "mutt_attach.h"
59 #include "mutt_body.h"
60 #include "mutt_header.h"
61 #include "mutt_logging.h"
62 #include "mutt_parse.h"
63 #include "muttlib.h"
64 #include "ncrypt/ncrypt.h"
65 #include "options.h"
66 #include "pattern.h"
67 #include "protos.h"
68 #include "recvattach.h"
69 #include "rfc3676.h"
70 #include "sendlib.h"
71 #include "smtp.h"
72 #include "sort.h"
73 #ifdef USE_NNTP
74 #include "mx.h"
75 #include "nntp/nntp.h"
76 #endif
77 #ifdef MIXMASTER
78 #include "remailer.h"
79 #endif
80 #ifdef USE_NOTMUCH
81 #include "notmuch/mutt_notmuch.h"
82 #endif
83 #ifdef USE_IMAP
84 #include "imap/imap.h"
85 #endif
86 #ifdef USE_AUTOCRYPT
87 #include "autocrypt/autocrypt.h"
88 #endif
89
90 /* These Config Variables are only used in send.c */
91 unsigned char C_AbortNoattach; ///< Config: Abort sending the email if attachments are missing
92 struct Regex *C_AbortNoattachRegex; ///< Config: Regex to match text indicating attachments are expected
93 unsigned char C_AbortNosubject; ///< Config: Abort creating the email if subject is missing
94 unsigned char C_AbortUnmodified; ///< Config: Abort the sending if the message hasn't been edited
95 bool C_AskFollowUp; ///< Config: (nntp) Ask the user for follow-up groups before editing
96 bool C_AskXCommentTo; ///< Config: (nntp) Ask the user for the 'X-Comment-To' field before editing
97 char *C_ContentType; ///< Config: Default "Content-Type" for newly composed messages
98 bool C_CryptAutoencrypt; ///< Config: Automatically PGP encrypt all outgoing mail
99 bool C_CryptAutopgp;     ///< Config: Allow automatic PGP functions
100 bool C_CryptAutosign;    ///< Config: Automatically PGP sign all outgoing mail
101 bool C_CryptAutosmime;   ///< Config: Allow automatic SMIME functions
102 bool C_CryptReplyencrypt; ///< Config: Encrypt replies to encrypted messages
103 bool C_CryptReplysign;    ///< Config: Sign replies to signed messages
104 bool C_CryptReplysignencrypted; ///< Config: Sign replies to encrypted messages
105 char *C_EmptySubject; ///< Config: Subject to use when replying to an email with none
106 bool C_FastReply; ///< Config: Don't prompt for the recipients and subject when replying/forwarding
107 unsigned char C_FccAttach; ///< Config: Save send message with all their attachments
108 bool C_FccBeforeSend;      ///< Config: Save FCCs before sending the message
109 bool C_FccClear; ///< Config: Save sent messages unencrypted and unsigned
110 bool C_FollowupTo; ///< Config: Add the 'Mail-Followup-To' header is generated when sending mail
111 char *C_ForwardAttributionIntro; ///< Config: Prefix message for forwarded messages
112 char *C_ForwardAttributionTrailer; ///< Config: Suffix message for forwarded messages
113 unsigned char C_ForwardEdit; ///< Config: Automatically start the editor when forwarding a message
114 char *C_ForwardFormat; ///< Config: printf-like format string to control the subject when forwarding a message
115 bool C_ForwardReferences; ///< Config: Set the 'In-Reply-To' and 'References' headers when forwarding a message
116 bool C_Hdrs;              ///< Config: Add custom headers to outgoing mail
117 unsigned char C_HonorFollowupTo; ///< Config: Honour the 'Mail-Followup-To' header when group replying
118 bool C_IgnoreListReplyTo; ///< Config: Ignore the 'Reply-To' header when using `<reply>` on a mailing list
119 unsigned char C_Include; ///< Config: Include a copy of the email that's being replied to
120 bool C_Metoo; ///< Config: Remove the user's address from the list of recipients
121 bool C_NmRecord; ///< Config: (notmuch) If the 'record' mailbox (sent mail) should be indexed
122 bool C_PgpReplyinline; ///< Config: Reply using old-style inline PGP messages (not recommended)
123 char *C_PostIndentString; ///< Config: Suffix message to add after reply text
124 bool C_PostponeEncrypt;   ///< Config: Self-encrypt postponed messages
125 char *C_PostponeEncryptAs; ///< Config: Fallback encryption key for postponed messages
126 unsigned char C_Recall; ///< Config: Recall postponed mesaages when asked to compose a message
127 bool C_ReplySelf; ///< Config: Really reply to yourself, when replying to your own email
128 unsigned char C_ReplyTo; ///< Config: Address to use as a 'Reply-To' header
129 bool C_ReplyWithXorig; ///< Config: Create 'From' header from 'X-Original-To' header
130 bool C_ReverseName; ///< Config: Set the 'From' from the address the email was sent to
131 bool C_ReverseRealname; ///< Config: Set the 'From' from the full 'To' address the email was sent to
132 bool C_SigDashes;  ///< Config: Insert '-- ' before the signature
133 char *C_Signature; ///< Config: File containing a signature to append to all mail
134 bool C_SigOnTop;   ///< Config: Insert the signature before the quoted text
135 bool C_UseFrom;    ///< Config: Set the 'From' header for outgoing mail
136
137 /**
138  * append_signature - Append a signature to an email
139  * @param fp File to write to
140  */
141 static void append_signature(FILE *fp)
142 {
143   FILE *fp_tmp = NULL;
144   pid_t pid;
145
146   if (C_Signature && (fp_tmp = mutt_open_read(C_Signature, &pid)))
147   {
148     if (C_SigDashes)
149       fputs("\n-- \n", fp);
150     mutt_file_copy_stream(fp_tmp, fp);
151     mutt_file_fclose(&fp_tmp);
152     if (pid != -1)
153       mutt_wait_filter(pid);
154   }
155 }
156
157 /**
158  * remove_user - Remove any address which matches the current user
159  * @param al         List of addresses
160  * @param leave_only If set, don't remove the user's address if it it the only
161  *                   one in the list
162  */
163 static void remove_user(struct AddressList *al, bool leave_only)
164 {
165   struct Address *a = NULL, *tmp = NULL;
166   TAILQ_FOREACH_SAFE(a, al, entries, tmp)
167   {
168     if (mutt_addr_is_user(a) && (!leave_only || TAILQ_NEXT(a, entries)))
169     {
170       TAILQ_REMOVE(al, a, entries);
171       mutt_addr_free(&a);
172     }
173   }
174 }
175
176 /**
177  * add_mailing_lists - Search Address lists for mailing lists
178  * @param out Address list where to append matching mailing lists
179  * @param t   'To' Address list
180  * @param c   'Cc' Address list
181  */
182 static void add_mailing_lists(struct AddressList *out, const struct AddressList *t,
183                               const struct AddressList *c)
184 {
185   const struct AddressList *const als[] = { t, c };
186
187   for (size_t i = 0; i < mutt_array_size(als); ++i)
188   {
189     const struct AddressList *al = als[i];
190     struct Address *a = NULL;
191     TAILQ_FOREACH(a, al, entries)
192     {
193       if (!a->group && mutt_is_mail_list(a))
194       {
195         mutt_addrlist_append(out, mutt_addr_copy(a));
196       }
197     }
198   }
199 }
200
201 /**
202  * mutt_edit_address - Edit an email address
203  * @param[in,out] al          AddressList to edit
204  * @param[in]  field          Prompt for user
205  * @param[in]  expand_aliases If true, expand Address aliases
206  * @retval  0 Success
207  * @retval -1 Failure
208  */
209 int mutt_edit_address(struct AddressList *al, const char *field, bool expand_aliases)
210 {
211   char buf[8192];
212   char *err = NULL;
213   int idna_ok = 0;
214
215   do
216   {
217     buf[0] = '\0';
218     mutt_addrlist_to_local(al);
219     mutt_addrlist_write(buf, sizeof(buf), al, false);
220     if (mutt_get_field(field, buf, sizeof(buf), MUTT_ALIAS) != 0)
221       return -1;
222     mutt_addrlist_clear(al);
223     mutt_addrlist_parse2(al, buf);
224     if (expand_aliases)
225       mutt_expand_aliases(al);
226     idna_ok = mutt_addrlist_to_intl(al, &err);
227     if (idna_ok != 0)
228     {
229       mutt_error(_("Bad IDN: '%s'"), err);
230       FREE(&err);
231     }
232   } while (idna_ok != 0);
233   return 0;
234 }
235
236 /**
237  * edit_envelope - Edit Envelope fields
238  * @param en    Envelope to edit
239  * @param flags Flags, see #SendFlags
240  * @retval  0 Success
241  * @retval -1 Failure
242  */
243 static int edit_envelope(struct Envelope *en, SendFlags flags)
244 {
245   char buf[8192];
246
247 #ifdef USE_NNTP
248   if (OptNewsSend)
249   {
250     if (en->newsgroups)
251       mutt_str_strfcpy(buf, en->newsgroups, sizeof(buf));
252     else
253       buf[0] = '\0';
254     if (mutt_get_field("Newsgroups: ", buf, sizeof(buf), 0) != 0)
255       return -1;
256     FREE(&en->newsgroups);
257     en->newsgroups = mutt_str_strdup(buf);
258
259     if (en->followup_to)
260       mutt_str_strfcpy(buf, en->followup_to, sizeof(buf));
261     else
262       buf[0] = '\0';
263     if (C_AskFollowUp && (mutt_get_field("Followup-To: ", buf, sizeof(buf), 0) != 0))
264     {
265       return -1;
266     }
267     FREE(&en->followup_to);
268     en->followup_to = mutt_str_strdup(buf);
269
270     if (en->x_comment_to)
271       mutt_str_strfcpy(buf, en->x_comment_to, sizeof(buf));
272     else
273       buf[0] = '\0';
274     if (C_XCommentTo && C_AskXCommentTo &&
275         (mutt_get_field("X-Comment-To: ", buf, sizeof(buf), 0) != 0))
276     {
277       return -1;
278     }
279     FREE(&en->x_comment_to);
280     en->x_comment_to = mutt_str_strdup(buf);
281   }
282   else
283 #endif
284   {
285     if ((mutt_edit_address(&en->to, _("To: "), true) == -1) || TAILQ_EMPTY(&en->to))
286       return -1;
287     if (C_Askcc && (mutt_edit_address(&en->cc, _("Cc: "), true) == -1))
288       return -1;
289     if (C_Askbcc && (mutt_edit_address(&en->bcc, _("Bcc: "), true) == -1))
290       return -1;
291     if (C_ReplyWithXorig && (flags & (SEND_REPLY | SEND_LIST_REPLY | SEND_GROUP_REPLY)) &&
292         (mutt_edit_address(&en->from, "From: ", true) == -1))
293     {
294       return -1;
295     }
296   }
297
298   if (en->subject)
299   {
300     if (C_FastReply)
301       return 0;
302     mutt_str_strfcpy(buf, en->subject, sizeof(buf));
303   }
304   else
305   {
306     const char *p = NULL;
307
308     buf[0] = '\0';
309     struct ListNode *uh = NULL;
310     STAILQ_FOREACH(uh, &UserHeader, entries)
311     {
312       size_t plen = mutt_str_startswith(uh->data, "subject:", CASE_IGNORE);
313       if (plen)
314       {
315         p = mutt_str_skip_email_wsp(uh->data + plen);
316         mutt_str_strfcpy(buf, p, sizeof(buf));
317       }
318     }
319   }
320
321   if ((mutt_get_field(_("Subject: "), buf, sizeof(buf), 0) != 0) ||
322       (!buf[0] &&
323        (query_quadoption(C_AbortNosubject, _("No subject, abort?")) != MUTT_NO)))
324   {
325     mutt_message(_("No subject, aborting"));
326     return -1;
327   }
328   mutt_str_replace(&en->subject, buf);
329
330   return 0;
331 }
332
333 #ifdef USE_NNTP
334 /**
335  * nntp_get_header - Get the trimmed header
336  * @param s Header line with leading whitespace
337  * @retval ptr Copy of string
338  *
339  * @note The caller should free the returned string.
340  */
341 static char *nntp_get_header(const char *s)
342 {
343   SKIPWS(s);
344   return mutt_str_strdup(s);
345 }
346 #endif
347
348 /**
349  * process_user_recips - Process the user headers
350  * @param env Envelope to populate
351  */
352 static void process_user_recips(struct Envelope *env)
353 {
354   struct ListNode *uh = NULL;
355   STAILQ_FOREACH(uh, &UserHeader, entries)
356   {
357     size_t plen;
358     if ((plen = mutt_str_startswith(uh->data, "to:", CASE_IGNORE)))
359       mutt_addrlist_parse(&env->to, uh->data + plen);
360     else if ((plen = mutt_str_startswith(uh->data, "cc:", CASE_IGNORE)))
361       mutt_addrlist_parse(&env->cc, uh->data + plen);
362     else if ((plen = mutt_str_startswith(uh->data, "bcc:", CASE_IGNORE)))
363       mutt_addrlist_parse(&env->bcc, uh->data + plen);
364 #ifdef USE_NNTP
365     else if ((plen = mutt_str_startswith(uh->data, "newsgroups:", CASE_IGNORE)))
366       env->newsgroups = nntp_get_header(uh->data + plen);
367     else if ((plen = mutt_str_startswith(uh->data, "followup-to:", CASE_IGNORE)))
368       env->followup_to = nntp_get_header(uh->data + plen);
369     else if ((plen = mutt_str_startswith(uh->data, "x-comment-to:", CASE_IGNORE)))
370       env->x_comment_to = nntp_get_header(uh->data + plen);
371 #endif
372   }
373 }
374
375 /**
376  * process_user_header - Process the user headers
377  * @param env Envelope to populate
378  */
379 static void process_user_header(struct Envelope *env)
380 {
381   struct ListNode *uh = NULL;
382   STAILQ_FOREACH(uh, &UserHeader, entries)
383   {
384     size_t plen;
385     if ((plen = mutt_str_startswith(uh->data, "from:", CASE_IGNORE)))
386     {
387       /* User has specified a default From: address.  Remove default address */
388       mutt_addrlist_clear(&env->from);
389       mutt_addrlist_parse(&env->from, uh->data + plen);
390     }
391     else if ((plen = mutt_str_startswith(uh->data, "reply-to:", CASE_IGNORE)))
392     {
393       mutt_addrlist_clear(&env->reply_to);
394       mutt_addrlist_parse(&env->reply_to, uh->data + plen);
395     }
396     else if ((plen = mutt_str_startswith(uh->data, "message-id:", CASE_IGNORE)))
397     {
398       char *tmp = mutt_extract_message_id(uh->data + plen, NULL);
399       if (mutt_addr_valid_msgid(tmp))
400       {
401         FREE(&env->message_id);
402         env->message_id = tmp;
403       }
404       else
405         FREE(&tmp);
406     }
407     else if (!mutt_str_startswith(uh->data, "to:", CASE_IGNORE) &&
408              !mutt_str_startswith(uh->data, "cc:", CASE_IGNORE) &&
409              !mutt_str_startswith(uh->data, "bcc:", CASE_IGNORE) &&
410 #ifdef USE_NNTP
411              !mutt_str_startswith(uh->data, "newsgroups:", CASE_IGNORE) &&
412              !mutt_str_startswith(uh->data, "followup-to:", CASE_IGNORE) &&
413              !mutt_str_startswith(uh->data, "x-comment-to:", CASE_IGNORE) &&
414 #endif
415              !mutt_str_startswith(uh->data, "supersedes:", CASE_IGNORE) &&
416              !mutt_str_startswith(uh->data, "subject:", CASE_IGNORE) &&
417              !mutt_str_startswith(uh->data, "return-path:", CASE_IGNORE))
418     {
419       mutt_list_insert_tail(&env->userhdrs, mutt_str_strdup(uh->data));
420     }
421   }
422 }
423
424 /**
425  * mutt_forward_intro - Add the "start of forwarded message" text
426  * @param m   Mailbox
427  * @param e Email
428  * @param fp  File to write to
429  */
430 void mutt_forward_intro(struct Mailbox *m, struct Email *e, FILE *fp)
431 {
432   if (!C_ForwardAttributionIntro || !fp)
433     return;
434
435   char buf[1024];
436   setlocale(LC_TIME, NONULL(C_AttributionLocale));
437   mutt_make_string(buf, sizeof(buf), 0, C_ForwardAttributionIntro, NULL, m, e);
438   setlocale(LC_TIME, "");
439   fputs(buf, fp);
440   fputs("\n\n", fp);
441 }
442
443 /**
444  * mutt_forward_trailer - Add a "end of forwarded message" text
445  * @param m   Mailbox
446  * @param e Email
447  * @param fp  File to write to
448  */
449 void mutt_forward_trailer(struct Mailbox *m, struct Email *e, FILE *fp)
450 {
451   if (!C_ForwardAttributionTrailer || !fp)
452     return;
453
454   char buf[1024];
455   setlocale(LC_TIME, NONULL(C_AttributionLocale));
456   mutt_make_string(buf, sizeof(buf), 0, C_ForwardAttributionTrailer, NULL, m, e);
457   setlocale(LC_TIME, "");
458   fputc('\n', fp);
459   fputs(buf, fp);
460   fputc('\n', fp);
461 }
462
463 /**
464  * include_forward - Write out a forwarded message
465  * @param m      Mailbox
466  * @param e      Email
467  * @param fp_out File to write to
468  * @retval  0 Success
469  * @retval -1 Failure
470  */
471 static int include_forward(struct Mailbox *m, struct Email *e, FILE *fp_out)
472 {
473   CopyHeaderFlags chflags = CH_DECODE;
474   CopyMessageFlags cmflags = MUTT_CM_NO_FLAGS;
475
476   mutt_parse_mime_message(m, e);
477   mutt_message_hook(m, e, MUTT_MESSAGE_HOOK);
478
479   if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT) && C_ForwardDecode)
480   {
481     /* make sure we have the user's passphrase before proceeding... */
482     if (!crypt_valid_passphrase(e->security))
483       return -1;
484   }
485
486   mutt_forward_intro(m, e, fp_out);
487
488   if (C_ForwardDecode)
489   {
490     cmflags |= MUTT_CM_DECODE | MUTT_CM_CHARCONV;
491     if (C_Weed)
492     {
493       chflags |= CH_WEED | CH_REORDER;
494       cmflags |= MUTT_CM_WEED;
495     }
496   }
497   if (C_ForwardQuote)
498     cmflags |= MUTT_CM_PREFIX;
499
500   mutt_copy_message(fp_out, m, e, cmflags, chflags, 0);
501   mutt_forward_trailer(m, e, fp_out);
502   return 0;
503 }
504
505 /**
506  * inline_forward_attachments - Add attachments to an email, inline
507  * @param[in]  m        Mailbox
508  * @param[in]  e        Current Email
509  * @param[out] plast    Pointer to the last Attachment
510  * @param[out] forwardq Result of asking the user to forward the attachments, e.g. #MUTT_YES
511  * @retval  0 Success
512  * @retval -1 Error
513  */
514 static int inline_forward_attachments(struct Mailbox *m, struct Email *e,
515                                       struct Body ***plast, int *forwardq)
516 {
517   struct Body **last = *plast;
518   struct Body *body = NULL;
519   struct Message *msg = NULL;
520   struct AttachCtx *actx = NULL;
521   int rc = 0, i;
522
523   mutt_parse_mime_message(m, e);
524   mutt_message_hook(m, e, MUTT_MESSAGE_HOOK);
525
526   msg = mx_msg_open(m, e->msgno);
527   if (!msg)
528     return -1;
529
530   actx = mutt_mem_calloc(1, sizeof(*actx));
531   actx->email = e;
532   actx->fp_root = msg->fp;
533
534   mutt_generate_recvattach_list(actx, actx->email, actx->email->content,
535                                 actx->fp_root, -1, 0, 0);
536
537   for (i = 0; i < actx->idxlen; i++)
538   {
539     body = actx->idx[i]->content;
540     if ((body->type != TYPE_MULTIPART) && !mutt_can_decode(body) &&
541         !((body->type == TYPE_APPLICATION) &&
542           ((mutt_str_strcasecmp(body->subtype, "pgp-signature") == 0) ||
543            (mutt_str_strcasecmp(body->subtype, "x-pkcs7-signature") == 0) ||
544            (mutt_str_strcasecmp(body->subtype, "pkcs7-signature") == 0))))
545     {
546       /* Ask the quadoption only once */
547       if (*forwardq == -1)
548       {
549         *forwardq = query_quadoption(C_ForwardAttachments,
550                                      /* L10N:
551                                         This is the prompt for $forward_attachments.
552                                         When inline forwarding ($mime_forward answered "no"), this prompts
553                                         whether to add non-decodable attachments from the original email.
554                                         Text/plain parts and the like will already be included in the
555                                         message contents, but other attachment, such as PDF files, will also
556                                         be added as attachments to the new mail, if this is answered yes.
557                                       */
558                                      _("Forward attachments?"));
559         if (*forwardq != MUTT_YES)
560         {
561           if (*forwardq == -1)
562             rc = -1;
563           goto cleanup;
564         }
565       }
566       if (mutt_body_copy(actx->idx[i]->fp, last, body) == -1)
567       {
568         rc = -1;
569         goto cleanup;
570       }
571       last = &((*last)->next);
572     }
573   }
574
575 cleanup:
576   *plast = last;
577   mx_msg_close(m, &msg);
578   mutt_actx_free(&actx);
579   return rc;
580 }
581
582 /**
583  * mutt_inline_forward - Forward attachments, inline
584  * @param m      Mailbox
585  * @param e_edit Email to alter
586  * @param e_cur  Current Email
587  * @param out    File
588  * @retval  0 Success
589  * @retval -1 Error
590  */
591 int mutt_inline_forward(struct Mailbox *m, struct Email *e_edit,
592                         struct Email *e_cur, FILE *out)
593 {
594   int i, forwardq = -1;
595   struct Body **last = NULL;
596
597   if (e_cur)
598     include_forward(m, e_cur, out);
599   else
600     for (i = 0; i < m->vcount; i++)
601       if (m->emails[m->v2r[i]]->tagged)
602         include_forward(m, m->emails[m->v2r[i]], out);
603
604   if (C_ForwardDecode && (C_ForwardAttachments != MUTT_NO))
605   {
606     last = &e_edit->content;
607     while (*last)
608       last = &((*last)->next);
609
610     if (e_cur)
611     {
612       if (inline_forward_attachments(m, e_cur, &last, &forwardq) != 0)
613         return -1;
614     }
615     else
616       for (i = 0; i < m->vcount; i++)
617         if (m->emails[m->v2r[i]]->tagged)
618         {
619           if (inline_forward_attachments(m, m->emails[m->v2r[i]], &last, &forwardq) != 0)
620             return -1;
621           if (forwardq == MUTT_NO)
622             break;
623         }
624   }
625
626   return 0;
627 }
628
629 /**
630  * mutt_make_attribution - Add "on DATE, PERSON wrote" header
631  * @param m      Mailbox
632  * @param e      Email
633  * @param fp_out File to write to
634  */
635 void mutt_make_attribution(struct Mailbox *m, struct Email *e, FILE *fp_out)
636 {
637   if (!C_Attribution || !fp_out)
638     return;
639
640   char buf[1024];
641   setlocale(LC_TIME, NONULL(C_AttributionLocale));
642   mutt_make_string(buf, sizeof(buf), 0, C_Attribution, NULL, m, e);
643   setlocale(LC_TIME, "");
644   fputs(buf, fp_out);
645   fputc('\n', fp_out);
646 }
647
648 /**
649  * mutt_make_post_indent - Add suffix to replied email text
650  * @param m      Mailbox
651  * @param e      Email
652  * @param fp_out File to write to
653  */
654 void mutt_make_post_indent(struct Mailbox *m, struct Email *e, FILE *fp_out)
655 {
656   if (!C_PostIndentString || !fp_out)
657     return;
658
659   char buf[256];
660   mutt_make_string(buf, sizeof(buf), 0, C_PostIndentString, NULL, m, e);
661   fputs(buf, fp_out);
662   fputc('\n', fp_out);
663 }
664
665 /**
666  * include_reply - Generate the reply text for an email
667  * @param m      Mailbox
668  * @param e      Email
669  * @param fp_out File to write to
670  * @retval  0 Success
671  * @retval -1 Failure
672  */
673 static int include_reply(struct Mailbox *m, struct Email *e, FILE *fp_out)
674 {
675   CopyMessageFlags cmflags =
676       MUTT_CM_PREFIX | MUTT_CM_DECODE | MUTT_CM_CHARCONV | MUTT_CM_REPLYING;
677   CopyHeaderFlags chflags = CH_DECODE;
678
679   if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT))
680   {
681     /* make sure we have the user's passphrase before proceeding... */
682     if (!crypt_valid_passphrase(e->security))
683       return -1;
684   }
685
686   mutt_parse_mime_message(m, e);
687   mutt_message_hook(m, e, MUTT_MESSAGE_HOOK);
688
689   mutt_make_attribution(m, e, fp_out);
690
691   if (!C_Header)
692     cmflags |= MUTT_CM_NOHEADER;
693   if (C_Weed)
694   {
695     chflags |= CH_WEED | CH_REORDER;
696     cmflags |= MUTT_CM_WEED;
697   }
698
699   mutt_copy_message(fp_out, m, e, cmflags, chflags, 0);
700
701   mutt_make_post_indent(m, e, fp_out);
702
703   return 0;
704 }
705
706 /**
707  * choose_default_to - Pick the best 'to:' value
708  * @param from From Address
709  * @param env  Envelope
710  * @retval ptr Addresses to use
711  */
712 static const struct AddressList *choose_default_to(const struct Address *from,
713                                                    const struct Envelope *env)
714 {
715   if (!C_ReplySelf && mutt_addr_is_user(from))
716   {
717     /* mail is from the user, assume replying to recipients */
718     return &env->to;
719   }
720   else
721   {
722     return &env->from;
723   }
724 }
725
726 /**
727  * default_to - Generate default email addresses
728  * @param[in,out] to      'To' address
729  * @param[in]     env     Envelope to populate
730  * @param[in]     flags   Flags, see #SendFlags
731  * @param[in]     hmfupto If true, add 'followup-to' address to 'to' address
732  * @retval  0 Success
733  * @retval -1 Aborted
734  */
735 static int default_to(struct AddressList *to, struct Envelope *env, SendFlags flags, int hmfupto)
736 {
737   char prompt[256];
738   const struct Address *from = TAILQ_FIRST(&env->from);
739   const struct Address *reply_to = TAILQ_FIRST(&env->reply_to);
740
741   if (flags && !TAILQ_EMPTY(&env->mail_followup_to) && (hmfupto == MUTT_YES))
742   {
743     mutt_addrlist_copy(to, &env->mail_followup_to, true);
744     return 0;
745   }
746
747   /* Exit now if we're setting up the default Cc list for list-reply
748    * (only set if Mail-Followup-To is present and honoured).  */
749   if (flags & SEND_LIST_REPLY)
750     return 0;
751
752   const struct AddressList *default_to = choose_default_to(from, env);
753
754   if (reply_to)
755   {
756     const bool from_is_reply_to = mutt_addr_cmp(from, reply_to);
757     const bool multiple_reply_to =
758         reply_to && TAILQ_NEXT(TAILQ_FIRST(&env->reply_to), entries);
759     if ((from_is_reply_to && !multiple_reply_to && !reply_to->personal) ||
760         (C_IgnoreListReplyTo && mutt_is_mail_list(reply_to) &&
761          (mutt_addrlist_search(reply_to, &env->to) ||
762           mutt_addrlist_search(reply_to, &env->cc))))
763     {
764       /* If the Reply-To: address is a mailing list, assume that it was
765        * put there by the mailing list, and use the From: address
766        *
767        * We also take the from header if our correspondent has a reply-to
768        * header which is identical to the electronic mail address given
769        * in his From header, and the reply-to has no display-name.  */
770       mutt_addrlist_copy(to, &env->from, false);
771     }
772     else if (!(from_is_reply_to && !multiple_reply_to) && (C_ReplyTo != MUTT_YES))
773     {
774       /* There are quite a few mailing lists which set the Reply-To:
775        * header field to the list address, which makes it quite impossible
776        * to send a message to only the sender of the message.  This
777        * provides a way to do that.  */
778       /* L10N: Asks whether the user respects the reply-to header.
779          If she says no, neomutt will reply to the from header's address instead. */
780       snprintf(prompt, sizeof(prompt), _("Reply to %s%s?"), reply_to->mailbox,
781                multiple_reply_to ? ",..." : "");
782       switch (query_quadoption(C_ReplyTo, prompt))
783       {
784         case MUTT_YES:
785           mutt_addrlist_copy(to, &env->reply_to, false);
786           break;
787
788         case MUTT_NO:
789           mutt_addrlist_copy(to, default_to, false);
790           break;
791
792         default:
793           return -1; /* abort */
794       }
795     }
796     else
797     {
798       mutt_addrlist_copy(to, &env->reply_to, false);
799     }
800   }
801   else
802   {
803     mutt_addrlist_copy(to, default_to, false);
804   }
805
806   return 0;
807 }
808
809 /**
810  * mutt_fetch_recips - Generate recpients for a reply email
811  * @param out   Envelope to populate
812  * @param in    Envelope of source email
813  * @param flags Flags, see #SendFlags
814  * @retval  0 Success
815  * @retval -1 Failure
816  */
817 int mutt_fetch_recips(struct Envelope *out, struct Envelope *in, SendFlags flags)
818 {
819   enum QuadOption hmfupto = MUTT_ABORT;
820   const struct Address *followup_to = TAILQ_FIRST(&in->mail_followup_to);
821
822   if ((flags & (SEND_LIST_REPLY | SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY)) && followup_to)
823   {
824     char prompt[256];
825     snprintf(prompt, sizeof(prompt), _("Follow-up to %s%s?"), followup_to->mailbox,
826              TAILQ_NEXT(TAILQ_FIRST(&in->mail_followup_to), entries) ? ",..." : "");
827
828     hmfupto = query_quadoption(C_HonorFollowupTo, prompt);
829     if (hmfupto == MUTT_ABORT)
830       return -1;
831   }
832
833   if (flags & SEND_LIST_REPLY)
834   {
835     add_mailing_lists(&out->to, &in->to, &in->cc);
836
837     if (followup_to && (hmfupto == MUTT_YES) &&
838         (default_to(&out->cc, in, flags & SEND_LIST_REPLY, (hmfupto == MUTT_YES)) == MUTT_ABORT))
839     {
840       return -1; /* abort */
841     }
842   }
843   else if (flags & SEND_TO_SENDER)
844   {
845     mutt_addrlist_copy(&out->to, &in->from, false);
846   }
847   else
848   {
849     if (default_to(&out->to, in, flags & (SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY),
850                    (hmfupto == MUTT_YES)) == -1)
851       return -1; /* abort */
852
853     if ((flags & (SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY)) &&
854         (!followup_to || (hmfupto != MUTT_YES)))
855     {
856       /* if(!mutt_addr_is_user(in->to)) */
857       if (flags & SEND_GROUP_REPLY)
858         mutt_addrlist_copy(&out->cc, &in->to, true);
859       else
860         mutt_addrlist_copy(&out->to, &in->cc, true);
861       mutt_addrlist_copy(&out->cc, &in->cc, true);
862     }
863   }
864   return 0;
865 }
866
867 /**
868  * add_references - Add the email's references to a list
869  * @param head List of references
870  * @param env    Envelope of message
871  */
872 static void add_references(struct ListHead *head, struct Envelope *env)
873 {
874   struct ListNode *np = NULL;
875
876   struct ListHead *src = !STAILQ_EMPTY(&env->references) ? &env->references : &env->in_reply_to;
877   STAILQ_FOREACH(np, src, entries)
878   {
879     mutt_list_insert_tail(head, mutt_str_strdup(np->data));
880   }
881 }
882
883 /**
884  * add_message_id - Add the email's message ID to a list
885  * @param head List of message IDs
886  * @param env  Envelope of message
887  */
888 static void add_message_id(struct ListHead *head, struct Envelope *env)
889 {
890   if (env->message_id)
891   {
892     mutt_list_insert_head(head, mutt_str_strdup(env->message_id));
893   }
894 }
895
896 /**
897  * mutt_fix_reply_recipients - Remove duplicate recipients
898  * @param env Envelope to fix
899  */
900 void mutt_fix_reply_recipients(struct Envelope *env)
901 {
902   if (!C_Metoo)
903   {
904     /* the order is important here.  do the CC: first so that if the
905      * the user is the only recipient, it ends up on the TO: field */
906     remove_user(&env->cc, TAILQ_EMPTY(&env->to));
907     remove_user(&env->to, TAILQ_EMPTY(&env->cc) || C_ReplySelf);
908   }
909
910   /* the CC field can get cluttered, especially with lists */
911   mutt_addrlist_dedupe(&env->to);
912   mutt_addrlist_dedupe(&env->cc);
913   mutt_addrlist_remove_xrefs(&env->to, &env->cc);
914
915   if (!TAILQ_EMPTY(&env->cc) && TAILQ_EMPTY(&env->to))
916   {
917     TAILQ_SWAP(&env->to, &env->cc, Address, entries);
918   }
919 }
920
921 /**
922  * mutt_make_forward_subject - Create a subject for a forwarded email
923  * @param env Envelope for result
924  * @param m   Mailbox
925  * @param e Email
926  */
927 void mutt_make_forward_subject(struct Envelope *env, struct Mailbox *m, struct Email *e)
928 {
929   if (!env)
930     return;
931
932   char buf[256];
933
934   /* set the default subject for the message. */
935   mutt_make_string(buf, sizeof(buf), 0, NONULL(C_ForwardFormat), NULL, m, e);
936   mutt_str_replace(&env->subject, buf);
937 }
938
939 /**
940  * mutt_make_misc_reply_headers - Set subject for a reply
941  * @param env    Envelope for result
942  * @param curenv Envelope of source email
943  */
944 void mutt_make_misc_reply_headers(struct Envelope *env, struct Envelope *curenv)
945 {
946   if (!env || !curenv)
947     return;
948
949   /* This takes precedence over a subject that might have
950    * been taken from a List-Post header.  Is that correct?  */
951   if (curenv->real_subj)
952   {
953     FREE(&env->subject);
954     env->subject = mutt_mem_malloc(mutt_str_strlen(curenv->real_subj) + 5);
955     sprintf(env->subject, "Re: %s", curenv->real_subj);
956   }
957   else if (!env->subject)
958     env->subject = mutt_str_strdup(C_EmptySubject);
959 }
960
961 /**
962  * mutt_add_to_reference_headers - Generate references for a reply email
963  * @param env    Envelope for result
964  * @param curenv Envelope of source email
965  */
966 void mutt_add_to_reference_headers(struct Envelope *env, struct Envelope *curenv)
967 {
968   add_references(&env->references, curenv);
969   add_message_id(&env->references, curenv);
970   add_message_id(&env->in_reply_to, curenv);
971
972 #ifdef USE_NNTP
973   if (OptNewsSend && C_XCommentTo && !TAILQ_EMPTY(&curenv->from))
974     env->x_comment_to = mutt_str_strdup(mutt_get_name(TAILQ_FIRST(&curenv->from)));
975 #endif
976 }
977
978 /**
979  * make_reference_headers - Generate reference headers for an email
980  * @param el  List of source Emails
981  * @param env Envelope for result
982  */
983 static void make_reference_headers(struct EmailList *el, struct Envelope *env)
984 {
985   if (!el || !env || STAILQ_EMPTY(el))
986     return;
987
988   struct EmailNode *en = STAILQ_FIRST(el);
989   bool single = !STAILQ_NEXT(en, entries);
990
991   if (!single)
992   {
993     STAILQ_FOREACH(en, el, entries)
994     {
995       mutt_add_to_reference_headers(env, en->email->env);
996     }
997   }
998   else
999     mutt_add_to_reference_headers(env, en->email->env);
1000
1001   /* if there's more than entry in In-Reply-To (i.e. message has multiple
1002    * parents), don't generate a References: header as it's discouraged by
1003    * RFC2822, sect. 3.6.4 */
1004   if (!single && !STAILQ_EMPTY(&env->in_reply_to) &&
1005       STAILQ_NEXT(STAILQ_FIRST(&env->in_reply_to), entries))
1006   {
1007     mutt_list_free(&env->references);
1008   }
1009 }
1010
1011 /**
1012  * envelope_defaults - Fill in some defaults for a new email
1013  * @param env   Envelope for result
1014  * @param m     Mailbox
1015  * @param el    List of Emails to use
1016  * @param flags Flags, see #SendFlags
1017  * @retval  0 Success
1018  * @retval -1 Failure
1019  */
1020 static int envelope_defaults(struct Envelope *env, struct Mailbox *m,
1021                              struct EmailList *el, SendFlags flags)
1022 {
1023   if (!el || STAILQ_EMPTY(el))
1024     return -1;
1025
1026   struct EmailNode *en = STAILQ_FIRST(el);
1027   bool single = !STAILQ_NEXT(en, entries);
1028
1029   struct Envelope *curenv = en->email->env;
1030   if (!curenv)
1031     return -1;
1032
1033   if (flags & (SEND_REPLY | SEND_TO_SENDER))
1034   {
1035 #ifdef USE_NNTP
1036     if ((flags & SEND_NEWS))
1037     {
1038       /* in case followup set Newsgroups: with Followup-To: if it present */
1039       if (!env->newsgroups &&
1040           (mutt_str_strcasecmp(curenv->followup_to, "poster") != 0))
1041       {
1042         env->newsgroups = mutt_str_strdup(curenv->followup_to);
1043       }
1044     }
1045     else
1046 #endif
1047         if (!single)
1048     {
1049       STAILQ_FOREACH(en, el, entries)
1050       {
1051         if (mutt_fetch_recips(env, en->email->env, flags) == -1)
1052           return -1;
1053       }
1054     }
1055     else if (mutt_fetch_recips(env, curenv, flags) == -1)
1056       return -1;
1057
1058     if ((flags & SEND_LIST_REPLY) && TAILQ_EMPTY(&env->to))
1059     {
1060       mutt_error(_("No mailing lists found"));
1061       return -1;
1062     }
1063
1064     if (flags & SEND_REPLY)
1065     {
1066       mutt_make_misc_reply_headers(env, curenv);
1067       make_reference_headers(el, env);
1068     }
1069   }
1070   else if (flags & SEND_FORWARD)
1071   {
1072     mutt_make_forward_subject(env, m, en->email);
1073     if (C_ForwardReferences)
1074       make_reference_headers(el, env);
1075   }
1076
1077   return 0;
1078 }
1079
1080 /**
1081  * generate_body - Create a new email body
1082  * @param fp_tmp Stream for outgoing message
1083  * @param e      Email for outgoing message
1084  * @param flags  Compose mode, see #SendFlags
1085  * @param m      Mailbox
1086  * @param el     List of Emails to use
1087  * @retval  0 Success
1088  * @retval -1 Error
1089  */
1090 static int generate_body(FILE *fp_tmp, struct Email *e, SendFlags flags,
1091                          struct Mailbox *m, struct EmailList *el)
1092 {
1093   struct Body *tmp = NULL;
1094   struct EmailNode *en = NULL;
1095   bool single = true;
1096
1097   if (el)
1098     en = STAILQ_FIRST(el);
1099   if (en)
1100     single = !STAILQ_NEXT(en, entries);
1101
1102   /* An EmailList is required for replying and forwarding */
1103   if (!el && (flags & (SEND_REPLY | SEND_FORWARD)))
1104     return -1;
1105
1106   if (flags & SEND_REPLY)
1107   {
1108     enum QuadOption ans =
1109         query_quadoption(C_Include, _("Include message in reply?"));
1110     if (ans == MUTT_ABORT)
1111       return -1;
1112
1113     if (ans == MUTT_YES)
1114     {
1115       mutt_message(_("Including quoted message..."));
1116       if (single && en)
1117         include_reply(m, en->email, fp_tmp);
1118       else
1119       {
1120         STAILQ_FOREACH(en, el, entries)
1121         {
1122           if (include_reply(m, en->email, fp_tmp) == -1)
1123           {
1124             mutt_error(_("Could not include all requested messages"));
1125             return -1;
1126           }
1127           fputc('\n', fp_tmp);
1128         }
1129       }
1130     }
1131   }
1132   else if (flags & SEND_FORWARD)
1133   {
1134     enum QuadOption ans =
1135         query_quadoption(C_MimeForward, _("Forward as attachment?"));
1136     if (ans == MUTT_YES)
1137     {
1138       struct Body *last = e->content;
1139
1140       mutt_message(_("Preparing forwarded message..."));
1141
1142       while (last && last->next)
1143         last = last->next;
1144
1145       if (single && en)
1146       {
1147         tmp = mutt_make_message_attach(m, en->email, false);
1148         if (last)
1149           last->next = tmp;
1150         else
1151           e->content = tmp;
1152       }
1153       else
1154       {
1155         STAILQ_FOREACH(en, el, entries)
1156         {
1157           tmp = mutt_make_message_attach(m, en->email, false);
1158           if (last)
1159           {
1160             last->next = tmp;
1161             last = tmp;
1162           }
1163           else
1164           {
1165             last = tmp;
1166             e->content = tmp;
1167           }
1168         }
1169       }
1170     }
1171     else if ((ans != MUTT_ABORT) && en)
1172     {
1173       if (mutt_inline_forward(m, e, en->email, fp_tmp) != 0)
1174         return -1;
1175     }
1176     else
1177       return -1;
1178   }
1179   /* if (WithCrypto && (flags & SEND_KEY)) */
1180   else if (((WithCrypto & APPLICATION_PGP) != 0) && (flags & SEND_KEY))
1181   {
1182     struct Body *b = NULL;
1183
1184     if (((WithCrypto & APPLICATION_PGP) != 0) && !(b = crypt_pgp_make_key_attachment()))
1185     {
1186       return -1;
1187     }
1188
1189     b->next = e->content;
1190     e->content = b;
1191   }
1192
1193   mutt_clear_error();
1194
1195   return 0;
1196 }
1197
1198 /**
1199  * mutt_set_followup_to - Set followup-to field
1200  * @param env Envelope to modify
1201  */
1202 void mutt_set_followup_to(struct Envelope *env)
1203 {
1204   /* Only generate the Mail-Followup-To if the user has requested it, and
1205    * it hasn't already been set */
1206
1207   if (!C_FollowupTo)
1208     return;
1209 #ifdef USE_NNTP
1210   if (OptNewsSend)
1211   {
1212     if (!env->followup_to && env->newsgroups && (strrchr(env->newsgroups, ',')))
1213       env->followup_to = mutt_str_strdup(env->newsgroups);
1214     return;
1215   }
1216 #endif
1217
1218   if (TAILQ_EMPTY(&env->mail_followup_to))
1219   {
1220     if (mutt_is_list_recipient(false, env))
1221     {
1222       /* this message goes to known mailing lists, so create a proper
1223        * mail-followup-to header */
1224
1225       mutt_addrlist_copy(&env->mail_followup_to, &env->to, false);
1226       mutt_addrlist_copy(&env->mail_followup_to, &env->cc, true);
1227     }
1228
1229     /* remove ourselves from the mail-followup-to header */
1230     remove_user(&env->mail_followup_to, false);
1231
1232     /* If we are not subscribed to any of the lists in question, re-add
1233      * ourselves to the mail-followup-to header.  The mail-followup-to header
1234      * generated is a no-op with group-reply, but makes sure list-reply has the
1235      * desired effect.  */
1236
1237     if (!TAILQ_EMPTY(&env->mail_followup_to) &&
1238         !mutt_is_subscribed_list_recipient(false, env))
1239     {
1240       struct AddressList *al = NULL;
1241       if (!TAILQ_EMPTY(&env->reply_to))
1242         al = &env->reply_to;
1243       else if (!TAILQ_EMPTY(&env->from))
1244         al = &env->from;
1245
1246       if (al)
1247       {
1248         struct Address *a = NULL;
1249         TAILQ_FOREACH_REVERSE(a, al, AddressList, entries)
1250         {
1251           mutt_addrlist_prepend(&env->mail_followup_to, mutt_addr_copy(a));
1252         }
1253       }
1254       else
1255       {
1256         mutt_addrlist_prepend(&env->mail_followup_to, mutt_default_from());
1257       }
1258     }
1259
1260     mutt_addrlist_dedupe(&env->mail_followup_to);
1261   }
1262 }
1263
1264 /**
1265  * set_reverse_name - Try to set the 'from' field from the recipients
1266  * @param al  AddressList to prepend the found address
1267  * @param env Envelope to use
1268  *
1269  * Look through the recipients of the message we are replying to, and if we
1270  * find an address that matches $alternates, we use that as the default from
1271  * field
1272  */
1273 static void set_reverse_name(struct AddressList *al, struct Envelope *env)
1274 {
1275   struct Address *a = NULL;
1276   if (TAILQ_EMPTY(al))
1277   {
1278     TAILQ_FOREACH(a, &env->to, entries)
1279     {
1280       if (mutt_addr_is_user(a))
1281       {
1282         mutt_addrlist_append(al, mutt_addr_copy(a));
1283         break;
1284       }
1285     }
1286   }
1287
1288   if (TAILQ_EMPTY(al))
1289   {
1290     TAILQ_FOREACH(a, &env->cc, entries)
1291     {
1292       if (mutt_addr_is_user(a))
1293       {
1294         mutt_addrlist_append(al, mutt_addr_copy(a));
1295         break;
1296       }
1297     }
1298   }
1299
1300   if (TAILQ_EMPTY(al))
1301   {
1302     struct Address *from = TAILQ_FIRST(&env->from);
1303     if (from && mutt_addr_is_user(from))
1304     {
1305       mutt_addrlist_append(al, mutt_addr_copy(from));
1306     }
1307   }
1308
1309   if (!TAILQ_EMPTY(al))
1310   {
1311     /* when $reverse_realname is not set, clear the personal name so that it
1312      * may be set via a reply- or send-hook.  */
1313     if (!C_ReverseRealname)
1314       FREE(&TAILQ_FIRST(al)->personal);
1315   }
1316 }
1317
1318 /**
1319  * mutt_default_from - Get a default 'from' Address
1320  * @retval ptr Newly allocated Address
1321  */
1322 struct Address *mutt_default_from(void)
1323 {
1324   /* Note: We let $from override $realname here.
1325    *       Is this the right thing to do?
1326    */
1327
1328   if (C_From)
1329   {
1330     return mutt_addr_copy(C_From);
1331   }
1332   else if (C_UseDomain)
1333   {
1334     struct Address *addr = mutt_addr_new();
1335     mutt_str_asprintf(&addr->mailbox, "%s@%s", NONULL(Username), NONULL(mutt_fqdn(true)));
1336     return addr;
1337   }
1338   else
1339   {
1340     return mutt_addr_create(NULL, Username);
1341   }
1342 }
1343
1344 /**
1345  * send_message - Send an email
1346  * @param e Email
1347  * @retval  0 Success
1348  * @retval -1 Failure
1349  */
1350 static int send_message(struct Email *e)
1351 {
1352   struct Buffer *tempfile = NULL;
1353   int i = -1;
1354 #ifdef USE_SMTP
1355   short old_write_bcc;
1356 #endif
1357
1358   /* Write out the message in MIME form. */
1359   tempfile = mutt_buffer_pool_get();
1360   mutt_buffer_mktemp(tempfile);
1361   FILE *fp_tmp = mutt_file_fopen(mutt_b2s(tempfile), "w");
1362   if (!fp_tmp)
1363     goto cleanup;
1364
1365 #ifdef USE_SMTP
1366   old_write_bcc = C_WriteBcc;
1367   if (C_SmtpUrl)
1368     C_WriteBcc = false;
1369 #endif
1370 #ifdef MIXMASTER
1371   mutt_rfc822_write_header(fp_tmp, e->env, e->content, MUTT_WRITE_HEADER_NORMAL,
1372                            !STAILQ_EMPTY(&e->chain),
1373                            mutt_should_hide_protected_subject(e));
1374 #endif
1375 #ifndef MIXMASTER
1376   mutt_rfc822_write_header(fp_tmp, e->env, e->content, MUTT_WRITE_HEADER_NORMAL,
1377                            false, mutt_should_hide_protected_subject(e));
1378 #endif
1379 #ifdef USE_SMTP
1380   if (old_write_bcc)
1381     C_WriteBcc = true;
1382 #endif
1383
1384   fputc('\n', fp_tmp); /* tie off the header. */
1385
1386   if ((mutt_write_mime_body(e->content, fp_tmp) == -1))
1387     goto cleanup;
1388
1389   if (mutt_file_fclose(&fp_tmp) != 0)
1390   {
1391     mutt_perror(mutt_b2s(tempfile));
1392     unlink(mutt_b2s(tempfile));
1393     goto cleanup;
1394   }
1395
1396 #ifdef MIXMASTER
1397   if (!STAILQ_EMPTY(&e->chain))
1398   {
1399     i = mix_send_message(&e->chain, mutt_b2s(tempfile));
1400     goto cleanup;
1401   }
1402 #endif
1403
1404 #ifdef USE_NNTP
1405   if (OptNewsSend)
1406     goto sendmail;
1407 #endif
1408
1409 #ifdef USE_SMTP
1410   if (C_SmtpUrl)
1411   {
1412     i = mutt_smtp_send(&e->env->from, &e->env->to, &e->env->cc, &e->env->bcc,
1413                        mutt_b2s(tempfile), (e->content->encoding == ENC_8BIT));
1414     goto cleanup;
1415   }
1416 #endif
1417
1418 sendmail:
1419   i = mutt_invoke_sendmail(&e->env->from, &e->env->to, &e->env->cc, &e->env->bcc,
1420                            mutt_b2s(tempfile), (e->content->encoding == ENC_8BIT));
1421 cleanup:
1422   if (fp_tmp)
1423   {
1424     mutt_file_fclose(&fp_tmp);
1425     unlink(mutt_b2s(tempfile));
1426   }
1427   mutt_buffer_pool_release(&tempfile);
1428   return i;
1429 }
1430
1431 /**
1432  * mutt_encode_descriptions - rfc2047 encode the content-descriptions
1433  * @param b       Body of email
1434  * @param recurse If true, encode children parts
1435  */
1436 void mutt_encode_descriptions(struct Body *b, bool recurse)
1437 {
1438   for (struct Body *t = b; t; t = t->next)
1439   {
1440     if (t->description)
1441     {
1442       rfc2047_encode(&t->description, NULL, sizeof("Content-Description:"), C_SendCharset);
1443     }
1444     if (recurse && t->parts)
1445       mutt_encode_descriptions(t->parts, recurse);
1446   }
1447 }
1448
1449 /**
1450  * decode_descriptions - rfc2047 decode them in case of an error
1451  * @param b MIME parts to decode
1452  */
1453 static void decode_descriptions(struct Body *b)
1454 {
1455   for (struct Body *t = b; t; t = t->next)
1456   {
1457     if (t->description)
1458     {
1459       rfc2047_decode(&t->description);
1460     }
1461     if (t->parts)
1462       decode_descriptions(t->parts);
1463   }
1464 }
1465
1466 /**
1467  * fix_end_of_file - Ensure a file ends with a linefeed
1468  * @param data Name of file to fix
1469  */
1470 static void fix_end_of_file(const char *data)
1471 {
1472   FILE *fp = mutt_file_fopen(data, "a+");
1473   if (!fp)
1474     return;
1475   if (fseek(fp, -1, SEEK_END) >= 0)
1476   {
1477     int c = fgetc(fp);
1478     if (c != '\n')
1479       fputc('\n', fp);
1480   }
1481   mutt_file_fclose(&fp);
1482 }
1483
1484 /**
1485  * mutt_resend_message - Resend an email
1486  * @param fp    File containing email
1487  * @param ctx   Mailbox
1488  * @param e_cur Email to resend
1489  * @retval  0 Message was successfully sent
1490  * @retval -1 Message was aborted or an error occurred
1491  * @retval  1 Message was postponed
1492  */
1493 int mutt_resend_message(FILE *fp, struct Context *ctx, struct Email *e_cur)
1494 {
1495   struct Email *e_new = email_new();
1496
1497   if (mutt_prepare_template(fp, ctx->mailbox, e_new, e_cur, true) < 0)
1498   {
1499     email_free(&e_new);
1500     return -1;
1501   }
1502
1503   if (WithCrypto)
1504   {
1505     /* mutt_prepare_template doesn't always flip on an application bit.
1506      * so fix that here */
1507     if (!(e_new->security & (APPLICATION_SMIME | APPLICATION_PGP)))
1508     {
1509       if (((WithCrypto & APPLICATION_SMIME) != 0) && C_SmimeIsDefault)
1510         e_new->security |= APPLICATION_SMIME;
1511       else if (WithCrypto & APPLICATION_PGP)
1512         e_new->security |= APPLICATION_PGP;
1513       else
1514         e_new->security |= APPLICATION_SMIME;
1515     }
1516
1517     if (C_CryptOpportunisticEncrypt)
1518     {
1519       e_new->security |= SEC_OPPENCRYPT;
1520       crypt_opportunistic_encrypt(e_new);
1521     }
1522   }
1523
1524   struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
1525   el_add_email(&el, e_cur);
1526   int rc = ci_send_message(SEND_RESEND, e_new, NULL, ctx, &el);
1527   emaillist_clear(&el);
1528
1529   return rc;
1530 }
1531
1532 /**
1533  * is_reply - Is one email a reply to another?
1534  * @param reply Email to test
1535  * @param orig  Original email
1536  * @retval true  It is a reply
1537  * @retval false It is not a reply
1538  */
1539 static bool is_reply(struct Email *reply, struct Email *orig)
1540 {
1541   if (!reply || !reply->env || !orig || !orig->env)
1542     return false;
1543   return mutt_list_find(&orig->env->references, reply->env->message_id) ||
1544          mutt_list_find(&orig->env->in_reply_to, reply->env->message_id);
1545 }
1546
1547 /**
1548  * search_attach_keyword - Search an email for 'attachment' keywords
1549  * @param filename Filename
1550  * @retval true If the regex matches in the email
1551  *
1552  * Search an email for the regex in $abort_noattach_regex.
1553  * A match might indicate that the user should have attached something.
1554  *
1555  * @note Quoted lines (as defined by $quote_regex) are ignored
1556  */
1557 static bool search_attach_keyword(char *filename)
1558 {
1559   /* Search for the regex in C_AbortNoattachRegex within a file */
1560   if (!C_AbortNoattachRegex || !C_AbortNoattachRegex->regex || !C_QuoteRegex ||
1561       !C_QuoteRegex->regex)
1562   {
1563     return false;
1564   }
1565
1566   FILE *fp_att = mutt_file_fopen(filename, "r");
1567   if (!fp_att)
1568     return false;
1569
1570   char *inputline = mutt_mem_malloc(1024);
1571   bool found = false;
1572   while (!feof(fp_att))
1573   {
1574     fgets(inputline, 1024, fp_att);
1575     if (!mutt_is_quote_line(inputline, NULL) &&
1576         mutt_regex_match(C_AbortNoattachRegex, inputline))
1577     {
1578       found = true;
1579       break;
1580     }
1581   }
1582   FREE(&inputline);
1583   mutt_file_fclose(&fp_att);
1584   return found;
1585 }
1586
1587 /**
1588  * save_fcc - Save an Email to a 'sent mail' folder
1589  * @param[in]  e             Email to save
1590  * @param[in]  fcc           Folder to save to (can be comma-separated list)
1591  * @param[in]  clear_content Cleartext content of Email
1592  * @param[in]  pgpkeylist    List of pgp keys
1593  * @param[in]  flags         Send mode, see #SendFlags
1594  * @param[out] finalpath     Path of final folder
1595  * @retval  0 Success
1596  * @retval -1 Error
1597  */
1598 static int save_fcc(struct Email *e, struct Buffer *fcc, struct Body *clear_content,
1599                     char *pgpkeylist, SendFlags flags, char **finalpath)
1600 {
1601   int rc = 0;
1602   struct Body *save_content = NULL;
1603
1604   mutt_buffer_expand_path(fcc);
1605
1606   /* Don't save a copy when we are in batch-mode, and the FCC
1607    * folder is on an IMAP server: This would involve possibly lots
1608    * of user interaction, which is not available in batch mode.
1609    *
1610    * Note: A patch to fix the problems with the use of IMAP servers
1611    * from non-curses mode is available from Brendan Cully.  However,
1612    * I'd like to think a bit more about this before including it.  */
1613
1614 #ifdef USE_IMAP
1615   if ((flags & SEND_BATCH) && !mutt_buffer_is_empty(fcc) &&
1616       (imap_path_probe(mutt_b2s(fcc), NULL) == MUTT_IMAP))
1617   {
1618     mutt_buffer_reset(fcc);
1619     mutt_error(_("Fcc to an IMAP mailbox is not supported in batch mode"));
1620     return rc;
1621   }
1622 #endif
1623
1624   if (mutt_buffer_is_empty(fcc) || (mutt_str_strcmp("/dev/null", mutt_b2s(fcc)) == 0))
1625     return rc;
1626
1627   struct Body *tmpbody = e->content;
1628   struct Body *save_sig = NULL;
1629   struct Body *save_parts = NULL;
1630
1631   /* Before sending, we don't allow message manipulation because it
1632    * will break message signatures.  This is especially complicated by
1633    * Protected Headers. */
1634   if (!C_FccBeforeSend)
1635   {
1636     if ((WithCrypto != 0) && (e->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT)) && C_FccClear)
1637     {
1638       e->content = clear_content;
1639       e->security &= ~(SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT);
1640       mutt_env_free(&e->content->mime_headers);
1641     }
1642
1643     /* check to see if the user wants copies of all attachments */
1644     if (e->content->type == TYPE_MULTIPART)
1645     {
1646       if ((WithCrypto != 0) && (e->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT)) &&
1647           ((mutt_str_strcmp(e->content->subtype, "encrypted") == 0) ||
1648            (mutt_str_strcmp(e->content->subtype, "signed") == 0)))
1649       {
1650         if ((clear_content->type == TYPE_MULTIPART) &&
1651             (query_quadoption(C_FccAttach, _("Save attachments in Fcc?")) == MUTT_NO))
1652         {
1653           if (!(e->security & SEC_ENCRYPT) && (e->security & SEC_SIGN))
1654           {
1655             /* save initial signature and attachments */
1656             save_sig = e->content->parts->next;
1657             save_parts = clear_content->parts->next;
1658           }
1659
1660           /* this means writing only the main part */
1661           e->content = clear_content->parts;
1662
1663           if (mutt_protect(e, pgpkeylist, false) == -1)
1664           {
1665             /* we can't do much about it at this point, so
1666            * fallback to saving the whole thing to fcc */
1667             e->content = tmpbody;
1668             save_sig = NULL;
1669             goto full_fcc;
1670           }
1671
1672           save_content = e->content;
1673         }
1674       }
1675       else
1676       {
1677         if (query_quadoption(C_FccAttach, _("Save attachments in Fcc?")) == MUTT_NO)
1678           e->content = e->content->parts;
1679       }
1680     }
1681   }
1682
1683 full_fcc:
1684   if (e->content)
1685   {
1686     /* update received time so that when storing to a mbox-style folder
1687      * the From_ line contains the current time instead of when the
1688      * message was first postponed.  */
1689     e->received = mutt_date_epoch();
1690     rc = mutt_write_multiple_fcc(mutt_b2s(fcc), e, NULL, false, NULL, finalpath);
1691     while (rc && !(flags & SEND_BATCH))
1692     {
1693       mutt_clear_error();
1694       int choice = mutt_multi_choice(
1695           /* L10N: Called when saving to $record or Fcc failed after sending.
1696              (r)etry tries the same mailbox again.
1697              alternate (m)ailbox prompts for a different mailbox to try.
1698              (s)kip aborts saving.  */
1699           _("Fcc failed. (r)etry, alternate (m)ailbox, or (s)kip?"),
1700           /* L10N: These correspond to the "Fcc failed" multi-choice prompt
1701              (r)etry, alternate (m)ailbox, or (s)kip.
1702              Any similarity to famous leaders of the FSF is coincidental.  */
1703           _("rms"));
1704       switch (choice)
1705       {
1706         case 2: /* alternate (m)ailbox */
1707           /* L10N: This is the prompt to enter an "alternate (m)ailbox" when the
1708              initial Fcc fails.  */
1709           rc = mutt_buffer_enter_fname(_("Fcc mailbox"), fcc, true);
1710           if ((rc == -1) || mutt_buffer_is_empty(fcc))
1711           {
1712             rc = 0;
1713             break;
1714           }
1715           /* fall through */
1716
1717         case 1: /* (r)etry */
1718           rc = mutt_write_multiple_fcc(mutt_b2s(fcc), e, NULL, false, NULL, finalpath);
1719           break;
1720
1721         case -1: /* abort */
1722         case 3:  /* (s)kip */
1723           rc = 0;
1724           break;
1725       }
1726     }
1727   }
1728
1729   if (!C_FccBeforeSend)
1730   {
1731     e->content = tmpbody;
1732
1733     if ((WithCrypto != 0) && save_sig)
1734     {
1735       /* cleanup the second signature structures */
1736       if (save_content->parts)
1737       {
1738         mutt_body_free(&save_content->parts->next);
1739         save_content->parts = NULL;
1740       }
1741       mutt_body_free(&save_content);
1742
1743       /* restore old signature and attachments */
1744       e->content->parts->next = save_sig;
1745       e->content->parts->parts->next = save_parts;
1746     }
1747     else if ((WithCrypto != 0) && save_content)
1748     {
1749       /* destroy the new encrypted body. */
1750       mutt_body_free(&save_content);
1751     }
1752   }
1753
1754   return 0;
1755 }
1756
1757 /**
1758  * postpone_message - Save an Email for another day
1759  * @param e_post Email to postpone
1760  * @param e_cur  Current Email in the index
1761  * @param fcc    Folder for 'sent mail'
1762  * @param flags  Send mode, see #SendFlags
1763  * @retval  0 Success
1764  * @retval -1 Error
1765  */
1766 static int postpone_message(struct Email *e_post, struct Email *e_cur,
1767                             const char *fcc, SendFlags flags)
1768 {
1769   char *pgpkeylist = NULL;
1770   char *encrypt_as = NULL;
1771   struct Body *clear_content = NULL;
1772
1773   if (!C_Postponed)
1774   {
1775     mutt_error(_("Can't postpone.  $postponed is unset"));
1776     return -1;
1777   }
1778
1779   if (e_post->content->next)
1780     e_post->content = mutt_make_multipart(e_post->content);
1781
1782   mutt_encode_descriptions(e_post->content, true);
1783
1784   if ((WithCrypto != 0) && C_PostponeEncrypt &&
1785       (e_post->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)))
1786   {
1787     if (((WithCrypto & APPLICATION_PGP) != 0) && (e_post->security & APPLICATION_PGP))
1788       encrypt_as = C_PgpDefaultKey;
1789     else if (((WithCrypto & APPLICATION_SMIME) != 0) && (e_post->security & APPLICATION_SMIME))
1790       encrypt_as = C_SmimeDefaultKey;
1791     if (!encrypt_as)
1792       encrypt_as = C_PostponeEncryptAs;
1793
1794 #ifdef USE_AUTOCRYPT
1795     if (e_post->security & SEC_AUTOCRYPT)
1796     {
1797       if (mutt_autocrypt_set_sign_as_default_key(e_post))
1798       {
1799         e_post->content = mutt_remove_multipart(e_post->content);
1800         decode_descriptions(e_post->content);
1801         return -1;
1802       }
1803       encrypt_as = AutocryptDefaultKey;
1804     }
1805 #endif
1806
1807     if (encrypt_as)
1808     {
1809       pgpkeylist = mutt_str_strdup(encrypt_as);
1810       clear_content = e_post->content;
1811       if (mutt_protect(e_post, pgpkeylist, true) == -1)
1812       {
1813         FREE(&pgpkeylist);
1814         e_post->content = mutt_remove_multipart(e_post->content);
1815         decode_descriptions(e_post->content);
1816         return -1;
1817       }
1818
1819       FREE(&pgpkeylist);
1820
1821       mutt_encode_descriptions(e_post->content, false);
1822     }
1823   }
1824
1825   /* make sure the message is written to the right part of a maildir
1826    * postponed folder.  */
1827   e_post->read = false;
1828   e_post->old = false;
1829
1830   mutt_prepare_envelope(e_post->env, false);
1831   mutt_env_to_intl(e_post->env, NULL, NULL); /* Handle bad IDNAs the next time. */
1832
1833   if (mutt_write_fcc(NONULL(C_Postponed), e_post,
1834                      (e_cur && (flags & SEND_REPLY)) ? e_cur->env->message_id : NULL,
1835                      true, fcc, NULL) < 0)
1836   {
1837     if (clear_content)
1838     {
1839       mutt_body_free(&e_post->content);
1840       e_post->content = clear_content;
1841     }
1842     mutt_env_free(&e_post->content->mime_headers); /* protected headers */
1843     e_post->content = mutt_remove_multipart(e_post->content);
1844     decode_descriptions(e_post->content);
1845     mutt_unprepare_envelope(e_post->env);
1846     return -1;
1847   }
1848
1849   mutt_update_num_postponed();
1850
1851   if (clear_content)
1852     mutt_body_free(&clear_content);
1853
1854   return 0;
1855 }
1856
1857 /**
1858  * ci_send_message - Send an email
1859  * @param flags    Send mode, see #SendFlags
1860  * @param e_templ  Template to use for new message
1861  * @param tempfile File specified by -i or -H
1862  * @param ctx      Current mailbox
1863  * @param el       List of Emails to send
1864  * @retval  0 Message was successfully sent
1865  * @retval -1 Message was aborted or an error occurred
1866  * @retval  1 Message was postponed
1867  */
1868 int ci_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile,
1869                     struct Context *ctx, struct EmailList *el)
1870 {
1871   char buf[1024];
1872   struct Buffer fcc = mutt_buffer_make(0); /* where to copy this message */
1873   FILE *fp_tmp = NULL;
1874   struct Body *pbody = NULL;
1875   int i;
1876   bool free_clear_content = false;
1877
1878   struct Body *clear_content = NULL;
1879   char *pgpkeylist = NULL;
1880   /* save current value of "pgp_sign_as"  and "smime_default_key" */
1881   char *pgp_signas = NULL;
1882   char *smime_signas = NULL;
1883   const char *tag = NULL;
1884   char *err = NULL;
1885   char *ctype = NULL;
1886   char *finalpath = NULL;
1887   struct EmailNode *en = NULL;
1888   struct Email *e_cur = NULL;
1889
1890   if (el)
1891     en = STAILQ_FIRST(el);
1892   if (en)
1893     e_cur = STAILQ_NEXT(en, entries) ? NULL : en->email;
1894
1895   int rc = -1;
1896
1897 #ifdef USE_NNTP
1898   if (flags & SEND_NEWS)
1899     OptNewsSend = true;
1900   else
1901     OptNewsSend = false;
1902 #endif
1903
1904   if (!flags && !e_templ && (C_Recall != MUTT_NO) &&
1905       mutt_num_postponed(ctx ? ctx->mailbox : NULL, true))
1906   {
1907     /* If the user is composing a new message, check to see if there
1908      * are any postponed messages first.  */
1909     enum QuadOption ans =
1910         query_quadoption(C_Recall, _("Recall postponed message?"));
1911     if (ans == MUTT_ABORT)
1912       return rc;
1913
1914     if (ans == MUTT_YES)
1915       flags |= SEND_POSTPONED;
1916   }
1917
1918   /* Allocate the buffer due to the long lifetime, but
1919    * pre-resize it to ensure there are no NULL data field issues */
1920   mutt_buffer_alloc(&fcc, 1024);
1921
1922   if (flags & SEND_POSTPONED)
1923   {
1924     if (WithCrypto & APPLICATION_PGP)
1925       pgp_signas = mutt_str_strdup(C_PgpSignAs);
1926     if (WithCrypto & APPLICATION_SMIME)
1927       smime_signas = mutt_str_strdup(C_SmimeSignAs);
1928   }
1929
1930   /* Delay expansion of aliases until absolutely necessary--shouldn't
1931    * be necessary unless we are prompting the user or about to execute a
1932    * send-hook.  */
1933
1934   if (!e_templ)
1935   {
1936     e_templ = email_new();
1937
1938     if (flags == SEND_POSTPONED)
1939     {
1940       rc = mutt_get_postponed(ctx, e_templ, &e_cur, &fcc);
1941       if (rc < 0)
1942       {
1943         flags = SEND_POSTPONED;
1944         goto cleanup;
1945       }
1946       flags = rc;
1947 #ifdef USE_NNTP
1948       /* If postponed message is a news article, it have
1949        * a "Newsgroups:" header line, then set appropriate flag.  */
1950       if (e_templ->env->newsgroups)
1951       {
1952         flags |= SEND_NEWS;
1953         OptNewsSend = true;
1954       }
1955       else
1956       {
1957         flags &= ~SEND_NEWS;
1958         OptNewsSend = false;
1959       }
1960 #endif
1961     }
1962
1963     if (flags & (SEND_POSTPONED | SEND_RESEND))
1964     {
1965       fp_tmp = mutt_file_fopen(e_templ->content->filename, "a+");
1966       if (!fp_tmp)
1967       {
1968         mutt_perror(e_templ->content->filename);
1969         goto cleanup;
1970       }
1971     }
1972
1973     if (!e_templ->env)
1974       e_templ->env = mutt_env_new();
1975   }
1976
1977   /* Parse and use an eventual list-post header */
1978   if ((flags & SEND_LIST_REPLY) && e_cur && e_cur->env && e_cur->env->list_post)
1979   {
1980     /* Use any list-post header as a template */
1981     mutt_parse_mailto(e_templ->env, NULL, e_cur->env->list_post);
1982     /* We don't let them set the sender's address. */
1983     mutt_addrlist_clear(&e_templ->env->from);
1984   }
1985
1986   if (!(flags & (SEND_KEY | SEND_POSTPONED | SEND_RESEND)))
1987   {
1988     /* When SEND_DRAFT_FILE is set, the caller has already
1989      * created the "parent" body structure.  */
1990     if (!(flags & SEND_DRAFT_FILE))
1991     {
1992       pbody = mutt_body_new();
1993       pbody->next = e_templ->content; /* don't kill command-line attachments */
1994       e_templ->content = pbody;
1995
1996       ctype = mutt_str_strdup(C_ContentType);
1997       if (!ctype)
1998         ctype = mutt_str_strdup("text/plain");
1999       mutt_parse_content_type(ctype, e_templ->content);
2000       FREE(&ctype);
2001       e_templ->content->unlink = true;
2002       e_templ->content->use_disp = false;
2003       e_templ->content->disposition = DISP_INLINE;
2004
2005       if (tempfile)
2006       {
2007         fp_tmp = mutt_file_fopen(tempfile, "a+");
2008         e_templ->content->filename = mutt_str_strdup(tempfile);
2009       }
2010       else
2011       {
2012         mutt_mktemp(buf, sizeof(buf));
2013         fp_tmp = mutt_file_fopen(buf, "w+");
2014         e_templ->content->filename = mutt_str_strdup(buf);
2015       }
2016     }
2017     else
2018       fp_tmp = mutt_file_fopen(e_templ->content->filename, "a+");
2019
2020     if (!fp_tmp)
2021     {
2022       mutt_debug(LL_DEBUG1, "can't create tempfile %s (errno=%d)\n",
2023                  e_templ->content->filename, errno);
2024       mutt_perror(e_templ->content->filename);
2025       goto cleanup;
2026     }
2027   }
2028
2029   /* this is handled here so that the user can match ~f in send-hook */
2030   if (e_cur && C_ReverseName && !(flags & (SEND_POSTPONED | SEND_RESEND)))
2031   {
2032     /* We shouldn't have to worry about alias expansion here since we are
2033      * either replying to a real or postponed message, therefore no aliases
2034      * should exist since the user has not had the opportunity to add
2035      * addresses to the list.  We just have to ensure the postponed messages
2036      * have their aliases expanded.  */
2037
2038     if (!TAILQ_EMPTY(&e_templ->env->from))
2039     {
2040       mutt_debug(LL_DEBUG5, "e_templ->env->from before set_reverse_name: %s\n",
2041                  TAILQ_FIRST(&e_templ->env->from)->mailbox);
2042       mutt_addrlist_clear(&e_templ->env->from);
2043     }
2044     set_reverse_name(&e_templ->env->from, e_cur->env);
2045   }
2046   if (e_cur && C_ReplyWithXorig && !(flags & (SEND_POSTPONED | SEND_RESEND | SEND_FORWARD)))
2047   {
2048     /* We shouldn't have to worry about freeing 'e_templ->env->from' before
2049      * setting it here since this code will only execute when doing some
2050      * sort of reply. The pointer will only be set when using the -H command
2051      * line option.
2052      *
2053      * If there is already a from address recorded in 'e_templ->env->from',
2054      * then it theoretically comes from C_ReverseName handling, and we don't use
2055      * the 'X-Orig-To header'.  */
2056     if (!TAILQ_EMPTY(&e_cur->env->x_original_to) && TAILQ_EMPTY(&e_templ->env->from))
2057     {
2058       mutt_addrlist_copy(&e_templ->env->from, &e_cur->env->x_original_to, false);
2059       mutt_debug(LL_DEBUG5, "e_templ->env->from extracted from X-Original-To: header: %s\n",
2060                  TAILQ_FIRST(&e_templ->env->from)->mailbox);
2061     }
2062   }
2063
2064   if (!(flags & (SEND_POSTPONED | SEND_RESEND)) &&
2065       !((flags & SEND_DRAFT_FILE) && C_ResumeDraftFiles))
2066   {
2067     if ((flags & (SEND_REPLY | SEND_FORWARD | SEND_TO_SENDER)) && ctx &&
2068         (envelope_defaults(e_templ->env, ctx->mailbox, el, flags) == -1))
2069     {
2070       goto cleanup;
2071     }
2072
2073     if (C_Hdrs)
2074       process_user_recips(e_templ->env);
2075
2076     /* Expand aliases and remove duplicates/crossrefs */
2077     mutt_expand_aliases_env(e_templ->env);
2078
2079     if (flags & SEND_REPLY)
2080       mutt_fix_reply_recipients(e_templ->env);
2081
2082 #ifdef USE_NNTP
2083     if ((flags & SEND_NEWS) && ctx && (ctx->mailbox->magic == MUTT_NNTP) &&
2084         !e_templ->env->newsgroups)
2085     {
2086       e_templ->env->newsgroups =
2087           mutt_str_strdup(((struct NntpMboxData *) ctx->mailbox->mdata)->group);
2088     }
2089 #endif
2090
2091     if (!(flags & (SEND_MAILX | SEND_BATCH)) &&
2092         !(C_Autoedit && C_EditHeaders) && !((flags & SEND_REPLY) && C_FastReply))
2093     {
2094       if (edit_envelope(e_templ->env, flags) == -1)
2095         goto cleanup;
2096     }
2097
2098     /* the from address must be set here regardless of whether or not
2099      * $use_from is set so that the '~P' (from you) operator in send-hook
2100      * patterns will work.  if $use_from is unset, the from address is killed
2101      * after send-hooks are evaluated */
2102
2103     const bool killfrom = TAILQ_EMPTY(&e_templ->env->from);
2104     if (killfrom)
2105     {
2106       mutt_addrlist_append(&e_templ->env->from, mutt_default_from());
2107     }
2108
2109     if ((flags & SEND_REPLY) && e_cur)
2110     {
2111       /* change setting based upon message we are replying to */
2112       mutt_message_hook(ctx ? ctx->mailbox : NULL, e_cur, MUTT_REPLY_HOOK);
2113
2114       /* set the replied flag for the message we are generating so that the
2115        * user can use ~Q in a send-hook to know when reply-hook's are also
2116        * being used.  */
2117       e_templ->replied = true;
2118     }
2119
2120     /* change settings based upon recipients */
2121
2122     mutt_message_hook(NULL, e_templ, MUTT_SEND_HOOK);
2123
2124     /* Unset the replied flag from the message we are composing since it is
2125      * no longer required.  This is done here because the FCC'd copy of
2126      * this message was erroneously get the 'R'eplied flag when stored in
2127      * a maildir-style mailbox.  */
2128     e_templ->replied = false;
2129
2130     /* $use_from and/or $from might have changed in a send-hook */
2131     if (killfrom)
2132     {
2133       mutt_addrlist_clear(&e_templ->env->from);
2134       if (C_UseFrom && !(flags & (SEND_POSTPONED | SEND_RESEND)))
2135         mutt_addrlist_append(&e_templ->env->from, mutt_default_from());
2136     }
2137
2138     if (C_Hdrs)
2139       process_user_header(e_templ->env);
2140
2141     if (flags & SEND_BATCH)
2142       mutt_file_copy_stream(stdin, fp_tmp);
2143
2144     if (C_SigOnTop && !(flags & (SEND_MAILX | SEND_KEY | SEND_BATCH)) &&
2145         C_Editor && (mutt_str_strcmp(C_Editor, "builtin") != 0))
2146     {
2147       append_signature(fp_tmp);
2148     }
2149
2150     /* include replies/forwarded messages, unless we are given a template */
2151     if (!tempfile && (ctx || !(flags & (SEND_REPLY | SEND_FORWARD))) &&
2152         (generate_body(fp_tmp, e_templ, flags, ctx ? ctx->mailbox : NULL, el) == -1))
2153     {
2154       goto cleanup;
2155     }
2156
2157     if (!C_SigOnTop && !(flags & (SEND_MAILX | SEND_KEY | SEND_BATCH)) &&
2158         C_Editor && (mutt_str_strcmp(C_Editor, "builtin") != 0))
2159     {
2160       append_signature(fp_tmp);
2161     }
2162   }
2163
2164   /* Only set format=flowed for new messages.  postponed/resent/draftfiles
2165    * should respect the original email.
2166    *
2167    * This is set here so that send-hook can be used to turn the option on.  */
2168   if (!(flags & (SEND_KEY | SEND_POSTPONED | SEND_RESEND | SEND_DRAFT_FILE)))
2169   {
2170     if (C_TextFlowed && (e_templ->content->type == TYPE_TEXT) &&
2171         (mutt_str_strcasecmp(e_templ->content->subtype, "plain") == 0))
2172     {
2173       mutt_param_set(&e_templ->content->parameter, "format", "flowed");
2174     }
2175   }
2176
2177   /* This hook is even called for postponed messages, and can, e.g., be
2178    * used for setting the editor, the sendmail path, or the
2179    * envelope sender.  */
2180   mutt_message_hook(NULL, e_templ, MUTT_SEND2_HOOK);
2181
2182   /* wait until now to set the real name portion of our return address so
2183    * that $realname can be set in a send-hook */
2184   {
2185     struct Address *from = TAILQ_FIRST(&e_templ->env->from);
2186     if (from && !from->personal && !(flags & (SEND_RESEND | SEND_POSTPONED)))
2187       from->personal = mutt_str_strdup(C_Realname);
2188   }
2189
2190   if (!(((WithCrypto & APPLICATION_PGP) != 0) && (flags & SEND_KEY)))
2191     mutt_file_fclose(&fp_tmp);
2192
2193   if (flags & SEND_MAILX)
2194   {
2195     if (mutt_builtin_editor(e_templ->content->filename, e_templ, e_cur) == -1)
2196       goto cleanup;
2197   }
2198   else if (!(flags & SEND_BATCH))
2199   {
2200     struct stat st;
2201     time_t mtime = mutt_file_decrease_mtime(e_templ->content->filename, NULL);
2202
2203     mutt_update_encoding(e_templ->content);
2204
2205     /* Select whether or not the user's editor should be called now.  We
2206      * don't want to do this when:
2207      * 1) we are sending a key/cert
2208      * 2) we are forwarding a message and the user doesn't want to edit it.
2209      *    This is controlled by the quadoption $forward_edit.  However, if
2210      *    both $edit_headers and $autoedit are set, we want to ignore the
2211      *    setting of $forward_edit because the user probably needs to add the
2212      *    recipients.  */
2213     if (!(flags & SEND_KEY) &&
2214         (((flags & SEND_FORWARD) == 0) || (C_EditHeaders && C_Autoedit) ||
2215          (query_quadoption(C_ForwardEdit, _("Edit forwarded message?")) == MUTT_YES)))
2216     {
2217       /* If the this isn't a text message, look for a mailcap edit command */
2218       if (mutt_needs_mailcap(e_templ->content))
2219       {
2220         if (!mutt_edit_attachment(e_templ->content))
2221           goto cleanup;
2222       }
2223       else if (!C_Editor || (mutt_str_strcmp("builtin", C_Editor) == 0))
2224         mutt_builtin_editor(e_templ->content->filename, e_templ, e_cur);
2225       else if (C_EditHeaders)
2226       {
2227         mutt_env_to_local(e_templ->env);
2228         mutt_edit_headers(C_Editor, e_templ->content->filename, e_templ, &fcc);
2229         mutt_env_to_intl(e_templ->env, NULL, NULL);
2230       }
2231       else
2232       {
2233         mutt_edit_file(C_Editor, e_templ->content->filename);
2234         if (stat(e_templ->content->filename, &st) == 0)
2235         {
2236           if (mtime != st.st_mtime)
2237             fix_end_of_file(e_templ->content->filename);
2238         }
2239         else
2240           mutt_perror(e_templ->content->filename);
2241       }
2242
2243       mutt_message_hook(NULL, e_templ, MUTT_SEND2_HOOK);
2244     }
2245
2246     if (!(flags & (SEND_POSTPONED | SEND_FORWARD | SEND_KEY | SEND_RESEND | SEND_DRAFT_FILE)))
2247     {
2248       if (stat(e_templ->content->filename, &st) == 0)
2249       {
2250         /* if the file was not modified, bail out now */
2251         if ((mtime == st.st_mtime) && !e_templ->content->next &&
2252             (query_quadoption(C_AbortUnmodified,
2253                               _("Abort unmodified message?")) == MUTT_YES))
2254         {
2255           mutt_message(_("Aborted unmodified message"));
2256           goto cleanup;
2257         }
2258       }
2259       else
2260         mutt_perror(e_templ->content->filename);
2261     }
2262   }
2263
2264   /* Set the message security unless:
2265    * 1) crypto support is not enabled (WithCrypto==0)
2266    * 2) pgp: header field was present during message editing with $edit_headers (e_templ->security != 0)
2267    * 3) we are resending a message
2268    * 4) we are recalling a postponed message (don't override the user's saved settings)
2269    * 5) we are in mailx mode
2270    * 6) we are in batch mode
2271    *
2272    * This is done after allowing the user to edit the message so that security
2273    * settings can be configured with send2-hook and $edit_headers.  */
2274   if ((WithCrypto != 0) && (e_templ->security == 0) &&
2275       !(flags & (SEND_BATCH | SEND_MAILX | SEND_POSTPONED | SEND_RESEND)))
2276   {
2277     if (
2278 #ifdef USE_AUTOCRYPT
2279         C_Autocrypt && C_AutocryptReply
2280 #else
2281         0
2282 #endif
2283         && e_cur && (e_cur->security & SEC_AUTOCRYPT))
2284     {
2285       e_templ->security |= (SEC_AUTOCRYPT | SEC_AUTOCRYPT_OVERRIDE);
2286     }
2287     else
2288     {
2289       if (C_CryptAutosign)
2290         e_templ->security |= SEC_SIGN;
2291       if (C_CryptAutoencrypt)
2292         e_templ->security |= SEC_ENCRYPT;
2293       if (C_CryptReplyencrypt && e_cur && (e_cur->security & SEC_ENCRYPT))
2294         e_templ->security |= SEC_ENCRYPT;
2295       if (C_CryptReplysign && e_cur && (e_cur->security & SEC_SIGN))
2296         e_templ->security |= SEC_SIGN;
2297       if (C_CryptReplysignencrypted && e_cur && (e_cur->security & SEC_ENCRYPT))
2298         e_templ->security |= SEC_SIGN;
2299       if (((WithCrypto & APPLICATION_PGP) != 0) &&
2300           ((e_templ->security & (SEC_ENCRYPT | SEC_SIGN)) || C_CryptOpportunisticEncrypt))
2301       {
2302         if (C_PgpAutoinline)
2303           e_templ->security |= SEC_INLINE;
2304         if (C_PgpReplyinline && e_cur && (e_cur->security & SEC_INLINE))
2305           e_templ->security |= SEC_INLINE;
2306       }
2307     }
2308
2309     if (e_templ->security || C_CryptOpportunisticEncrypt)
2310     {
2311       /* When replying / forwarding, use the original message's
2312        * crypto system.  According to the documentation,
2313        * smime_is_default should be disregarded here.
2314        *
2315        * Problem: At least with forwarding, this doesn't really
2316        * make much sense. Should we have an option to completely
2317        * disable individual mechanisms at run-time?  */
2318       if (e_cur)
2319       {
2320         if (((WithCrypto & APPLICATION_PGP) != 0) && C_CryptAutopgp &&
2321             (e_cur->security & APPLICATION_PGP))
2322         {
2323           e_templ->security |= APPLICATION_PGP;
2324         }
2325         else if (((WithCrypto & APPLICATION_SMIME) != 0) && C_CryptAutosmime &&
2326                  (e_cur->security & APPLICATION_SMIME))
2327         {
2328           e_templ->security |= APPLICATION_SMIME;
2329         }
2330       }
2331
2332       /* No crypto mechanism selected? Use availability + smime_is_default
2333        * for the decision.  */
2334       if (!(e_templ->security & (APPLICATION_SMIME | APPLICATION_PGP)))
2335       {
2336         if (((WithCrypto & APPLICATION_SMIME) != 0) && C_CryptAutosmime && C_SmimeIsDefault)
2337         {
2338           e_templ->security |= APPLICATION_SMIME;
2339         }
2340         else if (((WithCrypto & APPLICATION_PGP) != 0) && C_CryptAutopgp)
2341         {
2342           e_templ->security |= APPLICATION_PGP;
2343         }
2344         else if (((WithCrypto & APPLICATION_SMIME) != 0) && C_CryptAutosmime)
2345         {
2346           e_templ->security |= APPLICATION_SMIME;
2347         }
2348       }
2349     }
2350
2351     /* opportunistic encrypt relies on SMIME or PGP already being selected */
2352     if (C_CryptOpportunisticEncrypt)
2353     {
2354       /* If something has already enabled encryption, e.g. C_CryptAutoencrypt
2355        * or C_CryptReplyencrypt, then don't enable opportunistic encrypt for
2356        * the message.  */
2357       if (!(e_templ->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)))
2358       {
2359         e_templ->security |= SEC_OPPENCRYPT;
2360         crypt_opportunistic_encrypt(e_templ);
2361       }
2362     }
2363
2364     /* No permissible mechanisms found.  Don't sign or encrypt. */
2365     if (!(e_templ->security & (APPLICATION_SMIME | APPLICATION_PGP)))
2366       e_templ->security = SEC_NO_FLAGS;
2367   }
2368
2369   /* Deal with the corner case where the crypto module backend is not available.
2370    * This can happen if configured without pgp/smime and with gpgme, but
2371    * $crypt_use_gpgme is unset.  */
2372   if (e_templ->security && !crypt_has_module_backend(e_templ->security))
2373   {
2374     mutt_error(_(
2375         "No crypto backend configured.  Disabling message security setting."));
2376     e_templ->security = SEC_NO_FLAGS;
2377   }
2378
2379   /* specify a default fcc.  if we are in batchmode, only save a copy of
2380    * the message if the value of $copy is yes or ask-yes */
2381
2382   if (mutt_buffer_is_empty(&fcc) && !(flags & SEND_POSTPONED_FCC) &&
2383       (!(flags & SEND_BATCH) || (C_Copy & 0x1)))
2384   {
2385     /* set the default FCC */
2386     const bool killfrom = TAILQ_EMPTY(&e_templ->env->from);
2387     if (killfrom)
2388     {
2389       mutt_addrlist_append(&e_templ->env->from, mutt_default_from());
2390     }
2391     mutt_select_fcc(&fcc, e_templ);
2392     if (killfrom)
2393     {
2394       mutt_addrlist_clear(&e_templ->env->from);
2395     }
2396   }
2397
2398   mutt_rfc3676_space_stuff(e_templ);
2399
2400   mutt_update_encoding(e_templ->content);
2401
2402   if (!(flags & (SEND_MAILX | SEND_BATCH)))
2403   {
2404   main_loop:
2405
2406     mutt_buffer_pretty_mailbox(&fcc);
2407     i = mutt_compose_menu(e_templ, &fcc, e_cur,
2408                           ((flags & SEND_NO_FREE_HEADER) ? MUTT_COMPOSE_NOFREEHEADER : 0));
2409     if (i == -1)
2410     {
2411 /* abort */
2412 #ifdef USE_NNTP
2413       if (flags & SEND_NEWS)
2414         mutt_message(_("Article not posted"));
2415       else
2416 #endif
2417         mutt_message(_("Mail not sent"));
2418       goto cleanup;
2419     }
2420     else if (i == 1)
2421     {
2422       if (postpone_message(e_templ, e_cur, mutt_b2s(&fcc), flags) != 0)
2423         goto main_loop;
2424       mutt_message(_("Message postponed"));
2425       rc = 1;
2426       goto cleanup;
2427     }
2428   }
2429
2430 #ifdef USE_NNTP
2431   if (!(flags & SEND_NEWS))
2432 #endif
2433     if ((mutt_addrlist_count_recips(&e_templ->env->to) == 0) &&
2434         (mutt_addrlist_count_recips(&e_templ->env->cc) == 0) &&
2435         (mutt_addrlist_count_recips(&e_templ->env->bcc) == 0))
2436     {
2437       if (flags & SEND_BATCH)
2438       {
2439         puts(_("No recipients specified"));
2440         goto cleanup;
2441       }
2442
2443       mutt_error(_("No recipients specified"));
2444       goto main_loop;
2445     }
2446
2447   if (mutt_env_to_intl(e_templ->env, &tag, &err))
2448   {
2449     mutt_error(_("Bad IDN in '%s': '%s'"), tag, err);
2450     FREE(&err);
2451     if (flags & SEND_BATCH)
2452       goto cleanup;
2453     goto main_loop;
2454   }
2455
2456   if (!e_templ->env->subject && !(flags & SEND_BATCH) &&
2457       (query_quadoption(C_AbortNosubject, _("No subject, abort sending?")) != MUTT_NO))
2458   {
2459     /* if the abort is automatic, print an error message */
2460     if (C_AbortNosubject == MUTT_YES)
2461       mutt_error(_("No subject specified"));
2462     goto main_loop;
2463   }
2464 #ifdef USE_NNTP
2465   if ((flags & SEND_NEWS) && !e_templ->env->subject)
2466   {
2467     mutt_error(_("No subject specified"));
2468     goto main_loop;
2469   }
2470
2471   if ((flags & SEND_NEWS) && !e_templ->env->newsgroups)
2472   {
2473     mutt_error(_("No newsgroup specified"));
2474     goto main_loop;
2475   }
2476 #endif
2477
2478   if (!(flags & SEND_BATCH) && (C_AbortNoattach != MUTT_NO) &&
2479       !e_templ->content->next && (e_templ->content->type == TYPE_TEXT) &&
2480       (mutt_str_strcasecmp(e_templ->content->subtype, "plain") == 0) &&
2481       search_attach_keyword(e_templ->content->filename) &&
2482       (query_quadoption(C_AbortNoattach,
2483                         _("No attachments, cancel sending?")) != MUTT_NO))
2484   {
2485     /* if the abort is automatic, print an error message */
2486     if (C_AbortNoattach == MUTT_YES)
2487     {
2488       mutt_error(_("Message contains text matching "
2489                    "\"$abort_noattach_regex\". Not sending."));
2490     }
2491     goto main_loop;
2492   }
2493
2494   if (e_templ->content->next)
2495     e_templ->content = mutt_make_multipart(e_templ->content);
2496
2497   /* Ok, we need to do it this way instead of handling all fcc stuff in
2498    * one place in order to avoid going to main_loop with encoded "env"
2499    * in case of error.  Ugh.  */
2500
2501   mutt_encode_descriptions(e_templ->content, true);
2502
2503   /* Make sure that clear_content and free_clear_content are
2504    * properly initialized -- we may visit this particular place in
2505    * the code multiple times, including after a failed call to
2506    * mutt_protect().  */
2507
2508   clear_content = NULL;
2509   free_clear_content = false;
2510
2511   if (WithCrypto)
2512   {
2513     if (e_templ->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT))
2514     {
2515       /* save the decrypted attachments */
2516       clear_content = e_templ->content;
2517
2518       if ((crypt_get_keys(e_templ, &pgpkeylist, 0) == -1) ||
2519           (mutt_protect(e_templ, pgpkeylist, false) == -1))
2520       {
2521         e_templ->content = mutt_remove_multipart(e_templ->content);
2522
2523         FREE(&pgpkeylist);
2524
2525         decode_descriptions(e_templ->content);
2526         goto main_loop;
2527       }
2528       mutt_encode_descriptions(e_templ->content, false);
2529     }
2530
2531     /* at this point, e_templ->content is one of the following three things:
2532      * - multipart/signed.     In this case, clear_content is a child
2533      * - multipart/encrypted.  In this case, clear_content exists independently
2534      * - application/pgp.      In this case, clear_content exists independently
2535      * - something else.       In this case, it's the same as clear_content
2536      */
2537
2538     /* This is ugly -- lack of "reporting back" from mutt_protect(). */
2539
2540     if (clear_content && (e_templ->content != clear_content) &&
2541         (e_templ->content->parts != clear_content))
2542       free_clear_content = true;
2543   }
2544
2545   if (!OptNoCurses && !(flags & SEND_MAILX))
2546     mutt_message(_("Sending message..."));
2547
2548   mutt_prepare_envelope(e_templ->env, true);
2549
2550   if (C_FccBeforeSend)
2551     save_fcc(e_templ, &fcc, clear_content, pgpkeylist, flags, &finalpath);
2552
2553   i = send_message(e_templ);
2554   if (i < 0)
2555   {
2556     if (!(flags & SEND_BATCH))
2557     {
2558       if (!WithCrypto)
2559         ;
2560       else if ((e_templ->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)) ||
2561                ((e_templ->security & SEC_SIGN) && (e_templ->content->type == TYPE_APPLICATION)))
2562       {
2563         if (e_templ->content != clear_content)
2564         {
2565           mutt_body_free(&e_templ->content); /* destroy PGP data */
2566           e_templ->content = clear_content;  /* restore clear text. */
2567         }
2568       }
2569       else if ((e_templ->security & SEC_SIGN) && (e_templ->content->type == TYPE_MULTIPART))
2570       {
2571         mutt_body_free(&e_templ->content->parts->next); /* destroy sig */
2572         e_templ->content = mutt_remove_multipart(e_templ->content);
2573       }
2574
2575       FREE(&pgpkeylist);
2576       mutt_env_free(&e_templ->content->mime_headers); /* protected headers */
2577       e_templ->content = mutt_remove_multipart(e_templ->content);
2578       decode_descriptions(e_templ->content);
2579       mutt_unprepare_envelope(e_templ->env);
2580       FREE(&finalpath);
2581       goto main_loop;
2582     }
2583     else
2584     {
2585       puts(_("Could not send the message"));
2586       goto cleanup;
2587     }
2588   }
2589
2590   if (!C_FccBeforeSend)
2591     save_fcc(e_templ, &fcc, clear_content, pgpkeylist, flags, &finalpath);
2592
2593   if (!OptNoCurses && !(flags & SEND_MAILX))
2594   {
2595     mutt_message((i != 0) ? _("Sending in background") :
2596                             (flags & SEND_NEWS) ? _("Article posted") : /* USE_NNTP */
2597                                 _("Mail sent"));
2598 #ifdef USE_NOTMUCH
2599     if (C_NmRecord)
2600       nm_record_message(ctx ? ctx->mailbox : NULL, finalpath, e_cur);
2601 #endif
2602     mutt_sleep(0);
2603   }
2604
2605   if (WithCrypto)
2606     FREE(&pgpkeylist);
2607
2608   if ((WithCrypto != 0) && free_clear_content)
2609     mutt_body_free(&clear_content);
2610
2611   /* set 'replied' flag only if the user didn't change/remove
2612    * In-Reply-To: and References: headers during edit */
2613   if (flags & SEND_REPLY)
2614   {
2615     if (!(flags & SEND_POSTPONED) && ctx && ctx->mailbox)
2616     {
2617       STAILQ_FOREACH(en, el, entries)
2618       {
2619         mutt_set_flag(ctx->mailbox, en->email, MUTT_REPLIED, is_reply(en->email, e_templ));
2620       }
2621     }
2622   }
2623
2624   rc = 0;
2625
2626 cleanup:
2627   mutt_buffer_dealloc(&fcc);
2628
2629   if (flags & SEND_POSTPONED)
2630   {
2631     if (WithCrypto & APPLICATION_PGP)
2632     {
2633       FREE(&C_PgpSignAs);
2634       C_PgpSignAs = pgp_signas;
2635     }
2636     if (WithCrypto & APPLICATION_SMIME)
2637     {
2638       FREE(&C_SmimeSignAs);
2639       C_SmimeSignAs = smime_signas;
2640     }
2641   }
2642
2643   mutt_file_fclose(&fp_tmp);
2644   if (!(flags & SEND_NO_FREE_HEADER))
2645     email_free(&e_templ);
2646
2647   FREE(&finalpath);
2648   return rc;
2649 }