NeoMutt  2025-12-11-435-g4ac674
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
send.c
Go to the documentation of this file.
1
35
41
42#include "config.h"
43#include <errno.h>
44#include <locale.h>
45#include <stdbool.h>
46#include <stdio.h>
47#include <string.h>
48#include <sys/stat.h>
49#include <sys/types.h>
50#include <unistd.h>
51#include "mutt/lib.h"
52#include "address/lib.h"
53#include "config/lib.h"
54#include "email/lib.h"
55#include "core/lib.h"
56#include "alias/lib.h"
57#include "gui/lib.h"
58#include "mutt.h"
59#include "send.h"
60#include "attach/lib.h"
61#include "browser/lib.h"
62#include "compose/lib.h"
63#include "editor/lib.h"
64#include "expando/lib.h"
65#include "history/lib.h"
66#include "hooks/lib.h"
67#include "imap/lib.h"
68#include "index/lib.h"
69#include "ncrypt/lib.h"
70#include "pager/lib.h"
71#include "pattern/lib.h"
72#include "postpone/lib.h"
73#include "question/lib.h"
74#include "body.h"
75#include "expando_greeting.h"
76#include "expando_msgid.h"
77#include "globals.h"
78#include "header.h"
79#include "module_data.h"
80#include "multipart.h"
81#include "mutt_logging.h"
82#include "muttlib.h"
83#include "mx.h"
84#include "nntp/mdata.h"
85#include "sendlib.h"
86#include "sendmail.h"
87#include "smtp.h"
88#ifdef USE_NOTMUCH
89#include "notmuch/lib.h"
90#endif
91#ifdef USE_AUTOCRYPT
92#include "autocrypt/lib.h"
93#endif
94
100static void append_signature(FILE *fp, struct ConfigSubset *sub)
101{
102 const char *const c_signature = cs_subset_path(sub, "signature");
103 if (!c_signature)
104 return;
105
106 // If the user hasn't set $signature, don't warn them if it doesn't exist
107 struct Buffer *def_sig = buf_pool_get();
108 cs_str_initial_get(sub->cs, "signature", def_sig);
109 mutt_path_canon(def_sig, NeoMutt->home_dir, false);
110 bool notify_missing = !mutt_str_equal(c_signature, buf_string(def_sig));
111 buf_pool_release(&def_sig);
112
113 pid_t pid = 0;
114 FILE *fp_tmp = mutt_open_read(c_signature, &pid);
115 if (!fp_tmp)
116 {
117 if (notify_missing)
118 mutt_perror("%s", c_signature);
119 return;
120 }
121
122 const bool c_sig_dashes = cs_subset_bool(sub, "sig_dashes");
123 if (c_sig_dashes)
124 fputs("\n-- \n", fp);
125 mutt_file_copy_stream(fp_tmp, fp);
126 mutt_file_fclose(&fp_tmp);
127 if (pid != -1)
128 filter_wait(pid);
129}
130
137static void remove_user(struct AddressList *al, bool leave_only)
138{
139 struct Address *a = NULL, *tmp = NULL;
140 TAILQ_FOREACH_SAFE(a, al, entries, tmp)
141 {
142 if (mutt_addr_is_user(a) && (!leave_only || TAILQ_NEXT(a, entries)))
143 {
144 TAILQ_REMOVE(al, a, entries);
145 mutt_addr_free(&a);
146 }
147 }
148}
149
156static void add_mailing_lists(struct AddressList *out, const struct AddressList *t,
157 const struct AddressList *c)
158{
159 const struct AddressList *const als[] = { t, c };
160
161 for (size_t i = 0; i < countof(als); i++)
162 {
163 const struct AddressList *al = als[i];
164 struct Address *a = NULL;
165 TAILQ_FOREACH(a, al, entries)
166 {
167 if (!a->group && mutt_is_mail_list(a))
168 {
170 }
171 }
172 }
173}
174
183int mutt_edit_address(struct AddressList *al, const char *field, bool expand_aliases)
184{
185 int rc = 0;
186 struct Buffer *buf = buf_pool_get();
187 buf_alloc(buf, 8192);
188 char *err = NULL;
189 int idna_ok = 0;
190
191 do
192 {
194 buf_reset(buf);
195 mutt_addrlist_write(al, buf, false);
196 if (!buf_is_empty(buf))
197 buf_addstr(buf, ", ");
198
199 if (mw_get_field(field, buf, MUTT_COMP_NO_FLAGS, HC_ALIAS, &CompleteAliasOps, NULL) != 0)
200 {
201 rc = -1;
202 goto done;
203 }
206 if (expand_aliases)
208 idna_ok = mutt_addrlist_to_intl(al, &err);
209 if (idna_ok != 0)
210 {
211 mutt_error(_("Bad IDN: '%s'"), err);
212 FREE(&err);
213 }
214 } while (idna_ok != 0);
215
216done:
217 buf_pool_release(&buf);
218 return rc;
219}
220
229static int edit_envelope(struct Envelope *en, SendFlags flags, struct ConfigSubset *sub)
230{
231 int rc = -1;
232 struct Buffer *buf = buf_pool_get();
233 buf_alloc(buf, 8192);
234
235 if (OptNewsSend)
236 {
237 if (en->newsgroups)
238 buf_strcpy(buf, en->newsgroups);
239 else
240 buf_reset(buf);
241
242 if (mw_get_field("Newsgroups: ", buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0)
243 {
244 goto done;
245 }
247
248 if (en->followup_to)
249 buf_strcpy(buf, en->followup_to);
250 else
251 buf_reset(buf);
252
253 const bool c_ask_followup_to = cs_subset_bool(sub, "ask_followup_to");
254 if (c_ask_followup_to && (mw_get_field("Followup-To: ", buf, MUTT_COMP_NO_FLAGS,
255 HC_OTHER, NULL, NULL) != 0))
256 {
257 goto done;
258 }
260
261 if (en->x_comment_to)
262 buf_strcpy(buf, en->x_comment_to);
263 else
264 buf_reset(buf);
265
266 const bool c_x_comment_to = cs_subset_bool(sub, "x_comment_to");
267 const bool c_ask_x_comment_to = cs_subset_bool(sub, "ask_x_comment_to");
268 if (c_x_comment_to && c_ask_x_comment_to &&
269 (mw_get_field("X-Comment-To: ", buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0))
270 {
271 goto done;
272 }
274 }
275 else
276 {
277 const bool c_fast_reply = cs_subset_bool(sub, "fast_reply");
278 if (TAILQ_EMPTY(&en->to) || !c_fast_reply || (flags & SEND_REVIEW_TO))
279 {
280 if ((mutt_edit_address(&en->to, _("To: "), true) == -1))
281 goto done;
282 }
283
284 const bool c_ask_cc = cs_subset_bool(sub, "ask_cc");
285 if (TAILQ_EMPTY(&en->cc) || !c_fast_reply)
286 {
287 if (c_ask_cc && (mutt_edit_address(&en->cc, _("Cc: "), true) == -1))
288 goto done;
289 }
290
291 const bool c_ask_bcc = cs_subset_bool(sub, "ask_bcc");
292 if (TAILQ_EMPTY(&en->bcc) || !c_fast_reply)
293 {
294 if (c_ask_bcc && (mutt_edit_address(&en->bcc, _("Bcc: "), true) == -1))
295 goto done;
296 }
297
298 if (TAILQ_EMPTY(&en->to) && TAILQ_EMPTY(&en->cc) && TAILQ_EMPTY(&en->bcc))
299 {
300 mutt_warning(_("No recipients specified"));
301 goto done;
302 }
303
304 const bool c_reply_with_xorig = cs_subset_bool(sub, "reply_with_xorig");
305 if (c_reply_with_xorig && (flags & (SEND_REPLY | SEND_LIST_REPLY | SEND_GROUP_REPLY)) &&
306 (mutt_edit_address(&en->from, "From: ", true) == -1))
307 {
308 goto done;
309 }
310 }
311
313 ASSERT(md);
314
315 if (en->subject)
316 {
317 const bool c_fast_reply = cs_subset_bool(sub, "fast_reply");
318 if (c_fast_reply)
319 {
320 rc = 0;
321 goto done;
322 }
323 buf_strcpy(buf, en->subject);
324 }
325 else
326 {
327 const char *p = NULL;
328
329 buf_reset(buf);
330 struct ListNode *uh = NULL;
331 STAILQ_FOREACH(uh, &md->user_header, entries)
332 {
333 size_t plen = mutt_istr_startswith(uh->data, "subject:");
334 if (plen)
335 {
336 p = mutt_str_skip_email_wsp(uh->data + plen);
337 buf_strcpy(buf, p);
338 }
339 }
340 }
341
342 if ((mw_get_field(_("Subject: "), buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0) ||
343 (buf_is_empty(buf) &&
344 (query_quadoption(_("No subject, abort?"), sub, "abort_nosubject") != MUTT_NO)))
345 {
346 mutt_message(_("No subject, aborting"));
347 goto done;
348 }
350 rc = 0;
351
352done:
353 buf_pool_release(&buf);
354 return rc;
355}
356
364static char *nntp_get_header(const char *s)
365{
366 SKIPWS(s);
367 return mutt_str_dup(s);
368}
369
374static void process_user_recips(struct Envelope *env)
375{
377 ASSERT(md);
378
379 struct ListNode *uh = NULL;
380 STAILQ_FOREACH(uh, &md->user_header, entries)
381 {
382 size_t plen;
383 if ((plen = mutt_istr_startswith(uh->data, "to:")))
384 mutt_addrlist_parse(&env->to, uh->data + plen);
385 else if ((plen = mutt_istr_startswith(uh->data, "cc:")))
386 mutt_addrlist_parse(&env->cc, uh->data + plen);
387 else if ((plen = mutt_istr_startswith(uh->data, "bcc:")))
388 mutt_addrlist_parse(&env->bcc, uh->data + plen);
389 else if ((plen = mutt_istr_startswith(uh->data, "newsgroups:")))
390 env->newsgroups = nntp_get_header(uh->data + plen);
391 else if ((plen = mutt_istr_startswith(uh->data, "followup-to:")))
392 env->followup_to = nntp_get_header(uh->data + plen);
393 else if ((plen = mutt_istr_startswith(uh->data, "x-comment-to:")))
394 env->x_comment_to = nntp_get_header(uh->data + plen);
395 }
396}
397
402static void process_user_header(struct Envelope *env)
403{
405 ASSERT(md);
406
407 struct ListNode *uh = NULL;
408 STAILQ_FOREACH(uh, &md->user_header, entries)
409 {
410 size_t plen;
411 if ((plen = mutt_istr_startswith(uh->data, "from:")))
412 {
413 /* User has specified a default From: address. Remove default address */
415 mutt_addrlist_parse(&env->from, uh->data + plen);
416 }
417 else if ((plen = mutt_istr_startswith(uh->data, "reply-to:")))
418 {
420 mutt_addrlist_parse(&env->reply_to, uh->data + plen);
421 }
422 else if ((plen = mutt_istr_startswith(uh->data, "message-id:")))
423 {
424 char *tmp = mutt_extract_message_id(uh->data + plen, NULL);
425 if (mutt_addr_valid_msgid(tmp))
426 {
427 FREE(&env->message_id);
428 env->message_id = tmp;
429 }
430 else
431 {
432 FREE(&tmp);
433 }
434 }
435 else if (!mutt_istr_startswith(uh->data, "to:") &&
436 !mutt_istr_startswith(uh->data, "cc:") &&
437 !mutt_istr_startswith(uh->data, "bcc:") &&
438 !mutt_istr_startswith(uh->data, "newsgroups:") &&
439 !mutt_istr_startswith(uh->data, "followup-to:") &&
440 !mutt_istr_startswith(uh->data, "x-comment-to:") &&
441 !mutt_istr_startswith(uh->data, "supersedes:") &&
442 !mutt_istr_startswith(uh->data, "subject:") &&
443 !mutt_istr_startswith(uh->data, "return-path:"))
444 {
446 }
447 }
448}
449
456void mutt_forward_intro(struct Email *e, FILE *fp, struct ConfigSubset *sub)
457{
458 const struct Expando *c_forward_attribution_intro = cs_subset_expando(sub, "forward_attribution_intro");
459 if (!c_forward_attribution_intro || !fp)
460 return;
461
462 const char *const c_attribution_locale = cs_subset_string(sub, "attribution_locale");
463
464 struct Buffer *buf = buf_pool_get();
465 setlocale(LC_TIME, NONULL(c_attribution_locale));
466 mutt_make_string(buf, -1, c_forward_attribution_intro, NULL, -1, e,
468 setlocale(LC_TIME, "");
469 fputs(buf_string(buf), fp);
470 fputs("\n\n", fp);
471 buf_pool_release(&buf);
472}
473
480void mutt_forward_trailer(struct Email *e, FILE *fp, struct ConfigSubset *sub)
481{
482 const struct Expando *c_forward_attribution_trailer = cs_subset_expando(sub, "forward_attribution_trailer");
483 if (!c_forward_attribution_trailer || !fp)
484 return;
485
486 const char *const c_attribution_locale = cs_subset_string(sub, "attribution_locale");
487
488 struct Buffer *buf = buf_pool_get();
489 setlocale(LC_TIME, NONULL(c_attribution_locale));
490 mutt_make_string(buf, -1, c_forward_attribution_trailer, NULL, -1, e,
492 setlocale(LC_TIME, "");
493 fputc('\n', fp);
494 fputs(buf_string(buf), fp);
495 fputc('\n', fp);
496 buf_pool_release(&buf);
497}
498
508static int include_forward(struct Mailbox *m, struct Email *e, FILE *fp_out,
509 struct ConfigSubset *sub)
510{
511 CopyHeaderFlags chflags = CH_DECODE;
513
514 struct Message *msg = mx_msg_open(m, e);
515 if (!msg)
516 {
517 return -1;
518 }
521
522 const bool c_forward_decode = cs_subset_bool(sub, "forward_decode");
523 if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT) && c_forward_decode)
524 {
525 /* make sure we have the user's passphrase before proceeding... */
527 {
528 mx_msg_close(m, &msg);
529 return -1;
530 }
531 }
532
533 mutt_forward_intro(e, fp_out, sub);
534
535 if (c_forward_decode)
536 {
537 cmflags |= MUTT_CM_DECODE | MUTT_CM_CHARCONV;
538
539 const bool c_weed = cs_subset_bool(sub, "weed");
540 if (c_weed)
541 {
542 chflags |= CH_WEED | CH_REORDER;
543 cmflags |= MUTT_CM_WEED;
544 }
545 }
546
547 const bool c_forward_quote = cs_subset_bool(sub, "forward_quote");
548 if (c_forward_quote)
549 cmflags |= MUTT_CM_PREFIX;
550
551 mutt_copy_message(fp_out, e, msg, cmflags, chflags, 0);
552 mx_msg_close(m, &msg);
553 mutt_forward_trailer(e, fp_out, sub);
554 return 0;
555}
556
567static int inline_forward_attachments(struct Mailbox *m, struct Email *e,
568 struct Body ***plast, enum QuadOption *forwardq,
569 struct ConfigSubset *sub)
570{
571 struct Body **last = *plast;
572 struct Body *body = NULL;
573 struct AttachCtx *actx = NULL;
574 int rc = 0, i;
575
576 struct Message *msg = mx_msg_open(m, e);
577 if (!msg)
578 {
579 return -1;
580 }
581
584
585 actx = MUTT_MEM_CALLOC(1, struct AttachCtx);
586 actx->email = e;
587 actx->fp_root = msg->fp;
588
589 mutt_generate_recvattach_list(actx, actx->email, actx->email->body,
590 actx->fp_root, -1, 0, 0);
591
592 for (i = 0; i < actx->idxlen; i++)
593 {
594 body = actx->idx[i]->body;
595 if ((body->type != TYPE_MULTIPART) && mutt_prefer_as_attachment(body) &&
596 !((body->type == TYPE_APPLICATION) &&
597 (mutt_istr_equal(body->subtype, "pgp-signature") ||
598 mutt_istr_equal(body->subtype, "x-pkcs7-signature") ||
599 mutt_istr_equal(body->subtype, "pkcs7-signature"))))
600 {
601 /* Ask the quadoption only once */
602 if (*forwardq == MUTT_ABORT)
603 {
604 /* L10N: This is the prompt for $forward_attachments.
605 When inline forwarding ($mime_forward answered "no"), this prompts
606 whether to add non-decodable attachments from the original email.
607 Text/plain parts and the like will already be included in the
608 message contents, but other attachment, such as PDF files, will also
609 be added as attachments to the new mail, if this is answered yes. */
610 *forwardq = query_quadoption(_("Forward attachments?"), sub, "forward_attachments");
611 if (*forwardq != MUTT_YES)
612 {
613 if (*forwardq == -1)
614 rc = -1;
615 goto cleanup;
616 }
617 }
618 if (mutt_body_copy(actx->idx[i]->fp, last, body) == -1)
619 {
620 rc = -1;
621 goto cleanup;
622 }
623 last = &((*last)->next);
624 }
625 }
626
627cleanup:
628 *plast = last;
629 mx_msg_close(m, &msg);
630 mutt_actx_free(&actx);
631 return rc;
632}
633
641static void format_attribution(const struct Expando *exp, struct Email *e,
642 FILE *fp_out, struct ConfigSubset *sub)
643{
644 if (!exp || !fp_out)
645 return;
646
647 const char *const c_attribution_locale = cs_subset_string(sub, "attribution_locale");
648
649 struct Buffer *buf = buf_pool_get();
650 setlocale(LC_TIME, NONULL(c_attribution_locale));
651 mutt_make_string(buf, -1, exp, NULL, -1, e, MUTT_FORMAT_NO_FLAGS, NULL);
652 setlocale(LC_TIME, "");
653 fputs(buf_string(buf), fp_out);
654 fputc('\n', fp_out);
655 buf_pool_release(&buf);
656}
657
664void mutt_make_attribution_intro(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
665{
666 format_attribution(cs_subset_expando(sub, "attribution_intro"), e, fp_out, sub);
667}
668
675void mutt_make_attribution_trailer(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
676{
677 format_attribution(cs_subset_expando(sub, "attribution_trailer"), e, fp_out, sub);
678}
679
688static void mutt_make_greeting(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
689{
690 const struct Expando *c_greeting = cs_subset_expando(sub, "greeting");
691 if (!c_greeting || !fp_out)
692 return;
693
694 struct Buffer *buf = buf_pool_get();
695
697 buf->dsize, NeoMutt->env, buf);
698
699 fputs(buf_string(buf), fp_out);
700 fputc('\n', fp_out);
701 buf_pool_release(&buf);
702}
703
713static int include_reply(struct Mailbox *m, struct Email *e, FILE *fp_out,
714 struct ConfigSubset *sub)
715{
717 CopyHeaderFlags chflags = CH_DECODE;
718
719 if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT))
720 {
721 /* make sure we have the user's passphrase before proceeding... */
723 return -1;
724 }
725
726 struct Message *msg = mx_msg_open(m, e);
727 if (!msg)
728 {
729 return -1;
730 }
733
734 mutt_make_attribution_intro(e, fp_out, sub);
735
736 const bool c_header = cs_subset_bool(sub, "header");
737 if (!c_header)
738 cmflags |= MUTT_CM_NOHEADER;
739
740 const bool c_weed = cs_subset_bool(sub, "weed");
741 if (c_weed)
742 {
743 chflags |= CH_WEED | CH_REORDER;
744 cmflags |= MUTT_CM_WEED;
745 }
746
747 mutt_copy_message(fp_out, e, msg, cmflags, chflags, 0);
748 mx_msg_close(m, &msg);
749
750 mutt_make_attribution_trailer(e, fp_out, sub);
751
752 return 0;
753}
754
762static const struct AddressList *choose_default_to(const struct Address *from,
763 const struct Envelope *env,
764 struct ConfigSubset *sub)
765{
766 const bool c_reply_self = cs_subset_bool(sub, "reply_self");
767 if (!c_reply_self && mutt_addr_is_user(from))
768 {
769 /* mail is from the user, assume replying to recipients */
770 return &env->to;
771 }
772 else
773 {
774 return &env->from;
775 }
776}
777
788static int default_to(struct AddressList *to, struct Envelope *env,
789 SendFlags flags, int hmfupto, struct ConfigSubset *sub)
790{
791 const struct Address *from = TAILQ_FIRST(&env->from);
792 const struct Address *reply_to = TAILQ_FIRST(&env->reply_to);
793
794 if (flags && !TAILQ_EMPTY(&env->mail_followup_to) && (hmfupto == MUTT_YES))
795 {
796 mutt_addrlist_copy(to, &env->mail_followup_to, true);
797 return 0;
798 }
799
800 /* Exit now if we're setting up the default Cc list for list-reply
801 * (only set if Mail-Followup-To is present and honoured). */
802 if (flags & SEND_LIST_REPLY)
803 return 0;
804
805 const struct AddressList *default_to = choose_default_to(from, env, sub);
806
807 if (reply_to)
808 {
809 const bool from_is_reply_to = mutt_addr_cmp(from, reply_to);
810 const bool multiple_reply_to = reply_to &&
811 TAILQ_NEXT(TAILQ_FIRST(&env->reply_to), entries);
812
813 const bool c_ignore_list_reply_to = cs_subset_bool(sub, "ignore_list_reply_to");
814 const enum QuadOption c_reply_to = cs_subset_quad(sub, "reply_to");
815 if ((from_is_reply_to && !multiple_reply_to && !reply_to->personal) ||
816 (c_ignore_list_reply_to && mutt_is_mail_list(reply_to) &&
817 (mutt_addrlist_search(&env->to, reply_to) || mutt_addrlist_search(&env->cc, reply_to))))
818 {
819 /* If the Reply-To: address is a mailing list, assume that it was
820 * put there by the mailing list, and use the From: address
821 *
822 * We also take the from header if our correspondent has a reply-to
823 * header which is identical to the electronic mail address given
824 * in his From header, and the reply-to has no display-name. */
825 mutt_addrlist_copy(to, &env->from, false);
826 }
827 else if (!(from_is_reply_to && !multiple_reply_to) && (c_reply_to != MUTT_YES))
828 {
829 char prompt[256] = { 0 };
830 /* There are quite a few mailing lists which set the Reply-To:
831 * header field to the list address, which makes it quite impossible
832 * to send a message to only the sender of the message. This
833 * provides a way to do that. */
834 /* L10N: Asks whether the user respects the reply-to header.
835 If she says no, neomutt will reply to the from header's address instead. */
836 snprintf(prompt, sizeof(prompt), _("Reply to %s%s?"),
837 buf_string(reply_to->mailbox), multiple_reply_to ? ",..." : "");
838 switch (query_quadoption(prompt, sub, "reply_to"))
839 {
840 case MUTT_YES:
841 mutt_addrlist_copy(to, &env->reply_to, false);
842 break;
843
844 case MUTT_NO:
845 mutt_addrlist_copy(to, default_to, false);
846 break;
847
848 default:
849 return -1; /* abort */
850 }
851 }
852 else
853 {
854 mutt_addrlist_copy(to, &env->reply_to, false);
855 }
856 }
857 else
858 {
859 mutt_addrlist_copy(to, default_to, false);
860 }
861
862 return 0;
863}
864
874int mutt_fetch_recips(struct Envelope *out, struct Envelope *in,
875 SendFlags flags, struct ConfigSubset *sub)
876{
877 enum QuadOption hmfupto = MUTT_ABORT;
878 const struct Address *followup_to = TAILQ_FIRST(&in->mail_followup_to);
879
880 if ((flags & (SEND_LIST_REPLY | SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY)) && followup_to)
881 {
882 char prompt[256] = { 0 };
883 snprintf(prompt, sizeof(prompt), _("Follow-up to %s%s?"),
884 buf_string(followup_to->mailbox),
885 TAILQ_NEXT(TAILQ_FIRST(&in->mail_followup_to), entries) ? ",..." : "");
886
887 hmfupto = query_quadoption(prompt, sub, "honor_followup_to");
888 if (hmfupto == MUTT_ABORT)
889 return -1;
890 }
891
892 if (flags & SEND_LIST_REPLY)
893 {
894 add_mailing_lists(&out->to, &in->to, &in->cc);
895
896 if (followup_to && (hmfupto == MUTT_YES) &&
897 (default_to(&out->cc, in, flags & SEND_LIST_REPLY, (hmfupto == MUTT_YES), sub) == MUTT_ABORT))
898 {
899 return -1; /* abort */
900 }
901 }
902 else if (flags & SEND_TO_SENDER)
903 {
904 mutt_addrlist_copy(&out->to, &in->from, false);
905 }
906 else
907 {
908 if (default_to(&out->to, in, flags & (SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY),
909 (hmfupto == MUTT_YES), sub) == -1)
910 {
911 return -1; /* abort */
912 }
913
914 if ((flags & (SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY)) &&
915 (!followup_to || (hmfupto != MUTT_YES)))
916 {
917 /* if(!mutt_addr_is_user(in->to)) */
918 if (flags & SEND_GROUP_REPLY)
919 mutt_addrlist_copy(&out->cc, &in->to, true);
920 else
921 mutt_addrlist_copy(&out->to, &in->to, true);
922 mutt_addrlist_copy(&out->cc, &in->cc, true);
923 }
924 }
925 return 0;
926}
927
933static void add_references(struct ListHead *head, struct Envelope *env)
934{
935 struct ListHead *src = STAILQ_EMPTY(&env->references) ? &env->in_reply_to : &env->references;
936 mutt_list_copy_tail(head, src);
937}
938
944static void add_message_id(struct ListHead *head, struct Envelope *env)
945{
946 if (env->message_id)
947 {
949 }
950}
951
957void mutt_fix_reply_recipients(struct Envelope *env, struct ConfigSubset *sub)
958{
959 const bool c_me_too = cs_subset_bool(sub, "me_too");
960 if (!c_me_too)
961 {
962 const bool c_reply_self = cs_subset_bool(sub, "reply_self");
963
964 /* the order is important here. do the CC: first so that if the
965 * the user is the only recipient, it ends up on the TO: field */
966 remove_user(&env->cc, TAILQ_EMPTY(&env->to));
967 remove_user(&env->to, TAILQ_EMPTY(&env->cc) || c_reply_self);
968 }
969
970 /* the CC field can get cluttered, especially with lists */
973 mutt_addrlist_remove_xrefs(&env->to, &env->cc);
974
975 if (!TAILQ_EMPTY(&env->cc) && TAILQ_EMPTY(&env->to))
976 {
977 TAILQ_SWAP(&env->to, &env->cc, Address, entries);
978 }
979}
980
987void mutt_make_forward_subject(struct Envelope *env, struct Email *e, struct ConfigSubset *sub)
988{
989 if (!env)
990 return;
991
992 const struct Expando *c_forward_format = cs_subset_expando(sub, "forward_format");
993
994 struct Buffer *buf = buf_pool_get();
995 /* set the default subject for the message. */
996 mutt_make_string(buf, -1, c_forward_format, NULL, -1, e, MUTT_FORMAT_NO_FLAGS, NULL);
998 buf_pool_release(&buf);
999}
1000
1007void mutt_make_misc_reply_headers(struct Envelope *env, struct Envelope *env_cur,
1008 struct ConfigSubset *sub)
1009{
1010 if (!env || !env_cur)
1011 return;
1012
1013 /* This takes precedence over a subject that might have
1014 * been taken from a List-Post header. Is that correct? */
1015 if (env_cur->real_subj)
1016 {
1017 char *subj = NULL;
1018 mutt_str_asprintf(&subj, "Re: %s", env_cur->real_subj);
1019 mutt_env_set_subject(env, subj);
1020 FREE(&subj);
1021 }
1022 else if (!env->subject)
1023 {
1024 const char *const c_empty_subject = cs_subset_string(sub, "empty_subject");
1025 mutt_env_set_subject(env, c_empty_subject);
1026 }
1027}
1028
1035void mutt_add_to_reference_headers(struct Envelope *env, struct Envelope *env_cur,
1036 struct ConfigSubset *sub)
1037{
1038 add_references(&env->references, env_cur);
1039 add_message_id(&env->references, env_cur);
1040 add_message_id(&env->in_reply_to, env_cur);
1041
1042 const bool c_x_comment_to = cs_subset_bool(sub, "x_comment_to");
1043 if (OptNewsSend && c_x_comment_to && !TAILQ_EMPTY(&env_cur->from))
1045}
1046
1053static void make_reference_headers(struct EmailArray *ea, struct Envelope *env,
1054 struct ConfigSubset *sub)
1055{
1056 if (!ea || !env || ARRAY_EMPTY(ea))
1057 return;
1058
1059 struct Email **ep = NULL;
1060 ARRAY_FOREACH(ep, ea)
1061 {
1062 struct Email *e = *ep;
1064 }
1065
1066 /* if there's more than entry in In-Reply-To (i.e. message has multiple
1067 * parents), don't generate a References: header as it's discouraged by
1068 * RFC2822, sect. 3.6.4 */
1069 if ((ARRAY_SIZE(ea) > 1) && !STAILQ_EMPTY(&env->in_reply_to) &&
1071 {
1073 }
1074}
1075
1085static int envelope_defaults(struct Envelope *env, struct EmailArray *ea,
1086 SendFlags flags, struct ConfigSubset *sub)
1087{
1088 if (!ea || ARRAY_EMPTY(ea))
1089 return -1;
1090
1091 struct Email *e_cur = *ARRAY_GET(ea, 0);
1092 bool single = (ARRAY_SIZE(ea) == 1);
1093
1094 struct Envelope *env_cur = e_cur->env;
1095 if (!env_cur)
1096 return -1;
1097
1098 if (flags & (SEND_REPLY | SEND_TO_SENDER))
1099 {
1100 if ((flags & SEND_NEWS))
1101 {
1102 /* in case followup set Newsgroups: with Followup-To: if it present */
1103 if (!env->newsgroups && !mutt_istr_equal(env_cur->followup_to, "poster"))
1104 {
1105 env->newsgroups = mutt_str_dup(env_cur->followup_to);
1106 }
1107 }
1108 else if (!single)
1109 {
1110 struct Email **ep = NULL;
1111 ARRAY_FOREACH(ep, ea)
1112 {
1113 struct Email *e = *ep;
1114 if (mutt_fetch_recips(env, e->env, flags, sub) == -1)
1115 return -1;
1116 }
1117 }
1118 else if (mutt_fetch_recips(env, env_cur, flags, sub) == -1)
1119 {
1120 return -1;
1121 }
1122
1123 if ((flags & SEND_LIST_REPLY) && TAILQ_EMPTY(&env->to))
1124 {
1125 mutt_error(_("No mailing lists found"));
1126 return -1;
1127 }
1128
1129 if (flags & SEND_REPLY)
1130 {
1131 mutt_make_misc_reply_headers(env, env_cur, sub);
1132 make_reference_headers(ea, env, sub);
1133 }
1134 }
1135 else if (flags & SEND_FORWARD)
1136 {
1137 mutt_make_forward_subject(env, e_cur, sub);
1138
1139 const bool c_forward_references = cs_subset_bool(sub, "forward_references");
1140 if (c_forward_references)
1141 make_reference_headers(ea, env, sub);
1142 }
1143
1144 return 0;
1145}
1146
1158static int generate_body(FILE *fp_tmp, struct Email *e, SendFlags flags,
1159 struct Mailbox *m, struct EmailArray *ea, struct ConfigSubset *sub)
1160{
1161 /* An EmailList is required for replying and forwarding */
1162 if (!ea && (flags & (SEND_REPLY | SEND_FORWARD)))
1163 return -1;
1164
1165 if (flags & SEND_REPLY)
1166 {
1167 enum QuadOption ans = query_quadoption(_("Include message in reply?"), sub, "include");
1168 if (ans == MUTT_ABORT)
1169 return -1;
1170
1171 if (ans == MUTT_YES)
1172 {
1173 mutt_message(_("Including quoted message..."));
1174 struct Email **ep = NULL;
1175 size_t count = ARRAY_SIZE(ea) - 1;
1176 ARRAY_FOREACH(ep, ea)
1177 {
1178 if (include_reply(m, *ep, fp_tmp, sub) == -1)
1179 {
1180 mutt_error(_("Could not include all requested messages"));
1181 return -1;
1182 }
1183 if (ARRAY_FOREACH_IDX_ep < count)
1184 {
1185 fputc('\n', fp_tmp);
1186 }
1187 }
1188 }
1189 }
1190 else if (flags & SEND_FORWARD)
1191 {
1192 enum QuadOption ans = query_quadoption(_("Forward as attachment?"), sub, "mime_forward");
1193 if (ans == MUTT_YES)
1194 {
1195 struct Body *last = e->body;
1196
1197 mutt_message(_("Preparing forwarded message..."));
1198
1199 while (last && last->next)
1200 last = last->next;
1201
1202 struct Email **ep = NULL;
1203 ARRAY_FOREACH(ep, ea)
1204 {
1205 struct Body *tmp = mutt_make_message_attach(m, *ep, false, sub);
1206 if (last)
1207 {
1208 last->next = tmp;
1209 last = tmp;
1210 }
1211 else
1212 {
1213 last = tmp;
1214 e->body = tmp;
1215 }
1216 }
1217 }
1218 else if (ans != MUTT_ABORT)
1219 {
1220 enum QuadOption forwardq = MUTT_ABORT;
1221 struct Body **last = NULL;
1222
1223 const bool c_forward_decode = cs_subset_bool(sub, "forward_decode");
1224 const enum QuadOption c_forward_attachments = cs_subset_quad(sub, "forward_attachments");
1225 if (c_forward_decode && (c_forward_attachments != MUTT_NO))
1226 {
1227 last = &e->body;
1228 while (*last)
1229 last = &((*last)->next);
1230 }
1231
1232 struct Email **ep = NULL;
1233 ARRAY_FOREACH(ep, ea)
1234 {
1235 struct Email *e_cur = *ep;
1236 include_forward(m, e_cur, fp_tmp, sub);
1237 if (c_forward_decode && (c_forward_attachments != MUTT_NO))
1238 {
1239 if (inline_forward_attachments(m, e_cur, &last, &forwardq, sub) != 0)
1240 return -1;
1241 }
1242 }
1243 }
1244 else
1245 {
1246 return -1;
1247 }
1248 }
1249 else if (((WithCrypto & APPLICATION_PGP) != 0) && (flags & SEND_KEY))
1250 {
1251 struct Body *b = NULL;
1252
1253 if (((WithCrypto & APPLICATION_PGP) != 0) && !(b = crypt_pgp_make_key_attachment()))
1254 {
1255 return -1;
1256 }
1257
1258 b->next = e->body;
1259 e->body = b;
1260 }
1261
1263
1264 return 0;
1265}
1266
1272void mutt_set_followup_to(struct Envelope *env, struct ConfigSubset *sub)
1273{
1274 /* Only generate the Mail-Followup-To if the user has requested it, and
1275 * it hasn't already been set */
1276
1277 const bool c_followup_to = cs_subset_bool(sub, "followup_to");
1278 if (!c_followup_to)
1279 return;
1280 if (OptNewsSend)
1281 {
1282 if (!env->followup_to && env->newsgroups && (strrchr(env->newsgroups, ',')))
1283 env->followup_to = mutt_str_dup(env->newsgroups);
1284 return;
1285 }
1286
1287 if (TAILQ_EMPTY(&env->mail_followup_to))
1288 {
1289 if (mutt_is_list_recipient(false, env))
1290 {
1291 /* this message goes to known mailing lists, so create a proper
1292 * mail-followup-to header */
1293
1294 mutt_addrlist_copy(&env->mail_followup_to, &env->to, false);
1295 mutt_addrlist_copy(&env->mail_followup_to, &env->cc, true);
1296 }
1297
1298 /* remove ourselves from the mail-followup-to header */
1299 remove_user(&env->mail_followup_to, false);
1300
1301 /* If we are not subscribed to any of the lists in question, re-add
1302 * ourselves to the mail-followup-to header. The mail-followup-to header
1303 * generated is a no-op with group-reply, but makes sure list-reply has the
1304 * desired effect. */
1305
1306 if (!TAILQ_EMPTY(&env->mail_followup_to) &&
1308 {
1309 struct AddressList *al = NULL;
1310 if (!TAILQ_EMPTY(&env->reply_to))
1311 al = &env->reply_to;
1312 else if (!TAILQ_EMPTY(&env->from))
1313 al = &env->from;
1314
1315 if (al)
1316 {
1317 struct Address *a = NULL;
1318 TAILQ_FOREACH_REVERSE(a, al, AddressList, entries)
1319 {
1321 }
1322 }
1323 else
1324 {
1326 }
1327 }
1328
1330 }
1331}
1332
1343static void set_reverse_name(struct AddressList *al, struct Envelope *env,
1344 struct ConfigSubset *sub)
1345{
1346 struct Address *a = NULL;
1347 if (TAILQ_EMPTY(al))
1348 {
1349 TAILQ_FOREACH(a, &env->to, entries)
1350 {
1351 if (mutt_addr_is_user(a))
1352 {
1354 break;
1355 }
1356 }
1357 }
1358
1359 if (TAILQ_EMPTY(al))
1360 {
1361 TAILQ_FOREACH(a, &env->cc, entries)
1362 {
1363 if (mutt_addr_is_user(a))
1364 {
1366 break;
1367 }
1368 }
1369 }
1370
1371 if (TAILQ_EMPTY(al))
1372 {
1373 struct Address *from = TAILQ_FIRST(&env->from);
1374 if (from && mutt_addr_is_user(from))
1375 {
1377 }
1378 }
1379
1380 if (!TAILQ_EMPTY(al))
1381 {
1382 /* when $reverse_real_name is not set, clear the personal name so that it
1383 * may be set via a reply- or send-hook. */
1384
1385 const bool c_reverse_real_name = cs_subset_bool(sub, "reverse_real_name");
1386 if (!c_reverse_real_name)
1387 FREE(&TAILQ_FIRST(al)->personal);
1388 }
1389}
1390
1397{
1398 /* Note: We let $from override $real_name here.
1399 * Is this the right thing to do?
1400 */
1401
1402 const struct Address *c_from = cs_subset_address(sub, "from");
1403 if (c_from)
1404 {
1405 return mutt_addr_copy(c_from);
1406 }
1407
1408 char domain[1024] = { 0 };
1409 const char *mailbox = NeoMutt->username;
1410 const bool c_use_domain = cs_subset_bool(sub, "use_domain");
1411 if (c_use_domain)
1412 {
1413 snprintf(domain, sizeof(domain), "%s@%s", NONULL(NeoMutt->username),
1414 NONULL(mutt_fqdn(true, sub)));
1415 mailbox = domain;
1416 }
1417
1418 return mutt_addr_create(NULL, mailbox);
1419}
1420
1429static int invoke_mta(struct Mailbox *m, struct Email *e, struct ConfigSubset *sub)
1430{
1431 struct Buffer *tempfile = NULL;
1432 int rc = -1;
1433
1434 /* Write out the message in MIME form. */
1435 tempfile = buf_pool_get();
1436 buf_mktemp(tempfile);
1437 FILE *fp_tmp = mutt_file_fopen(buf_string(tempfile), "w");
1438 if (!fp_tmp)
1439 goto cleanup;
1440
1441 const bool c_write_bcc = cs_subset_bool(sub, "write_bcc");
1442 const char *const c_smtp_url = cs_subset_string(sub, "smtp_url");
1443 if (c_smtp_url)
1444 cs_subset_str_native_set(sub, "write_bcc", false, NULL);
1445
1447 false, mutt_should_hide_protected_subject(e), sub);
1448
1449 cs_subset_str_native_set(sub, "write_bcc", c_write_bcc, NULL);
1450
1451 fputc('\n', fp_tmp); /* tie off the header. */
1452
1453 if ((mutt_write_mime_body(e->body, fp_tmp, sub) == -1))
1454 goto cleanup;
1455
1456 if (mutt_file_fclose(&fp_tmp) != 0)
1457 {
1458 mutt_perror("%s", buf_string(tempfile));
1459 unlink(buf_string(tempfile));
1460 goto cleanup;
1461 }
1462
1463 if (OptNewsSend)
1464 goto sendmail;
1465
1466 if (c_smtp_url)
1467 {
1468 rc = mutt_smtp_send(&e->env->from, &e->env->to, &e->env->cc, &e->env->bcc,
1469 buf_string(tempfile), (e->body->encoding == ENC_8BIT), sub);
1470 goto cleanup;
1471 }
1472
1473sendmail:
1474 rc = mutt_invoke_sendmail(m, &e->env->from, &e->env->to, &e->env->cc, &e->env->bcc,
1475 buf_string(tempfile), (e->body->encoding == ENC_8BIT), sub);
1476cleanup:
1477 if (fp_tmp)
1478 {
1479 mutt_file_fclose(&fp_tmp);
1480 unlink(buf_string(tempfile));
1481 }
1482 buf_pool_release(&tempfile);
1483 return rc;
1484}
1485
1492void mutt_encode_descriptions(struct Body *b, bool recurse, struct ConfigSubset *sub)
1493{
1494 const struct Slist *const c_send_charset = cs_subset_slist(sub, "send_charset");
1495 for (struct Body *t = b; t; t = t->next)
1496 {
1497 if (t->description)
1498 {
1499 rfc2047_encode(&t->description, NULL, sizeof("Content-Description:"), c_send_charset);
1500 }
1501 if (recurse && t->parts)
1502 mutt_encode_descriptions(t->parts, recurse, sub);
1503 }
1504}
1505
1510static void decode_descriptions(struct Body *b)
1511{
1512 for (struct Body *t = b; t; t = t->next)
1513 {
1514 if (t->description)
1515 {
1516 rfc2047_decode(&t->description);
1517 }
1518 if (t->parts)
1519 decode_descriptions(t->parts);
1520 }
1521}
1522
1527static void fix_end_of_file(const char *data)
1528{
1529 FILE *fp = mutt_file_fopen(data, "a+");
1530 if (!fp)
1531 return;
1532
1533 if ((mutt_file_get_size_fp(fp) > 0) && mutt_file_seek(fp, -1, SEEK_END))
1534 {
1535 int c = fgetc(fp);
1536 if (c != '\n')
1537 fputc('\n', fp);
1538 }
1539 mutt_file_fclose(&fp);
1540}
1541
1552int mutt_resend_message(FILE *fp, struct Mailbox *m, struct Email *e_cur,
1553 struct ConfigSubset *sub)
1554{
1555 struct Email *e_new = email_new();
1556
1557 if (mutt_prepare_template(fp, m, e_new, e_cur, true) < 0)
1558 {
1559 email_free(&e_new);
1560 return -1;
1561 }
1562
1563 if (WithCrypto)
1564 {
1565 /* mutt_prepare_template doesn't always flip on an application bit.
1566 * so fix that here */
1567 if (!(e_new->security & (APPLICATION_SMIME | APPLICATION_PGP)))
1568 {
1569 const bool c_smime_is_default = cs_subset_bool(sub, "smime_is_default");
1570 if (((WithCrypto & APPLICATION_SMIME) != 0) && c_smime_is_default)
1571 e_new->security |= APPLICATION_SMIME;
1572 else if (WithCrypto & APPLICATION_PGP)
1573 e_new->security |= APPLICATION_PGP;
1574 else
1575 e_new->security |= APPLICATION_SMIME;
1576 }
1577
1578 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(sub, "crypt_opportunistic_encrypt");
1579 if (c_crypt_opportunistic_encrypt)
1580 {
1581 e_new->security |= SEC_OPPENCRYPT;
1583 }
1584 }
1585
1586 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
1587 ARRAY_ADD(&ea, e_cur);
1588 int rc = mutt_send_message(SEND_RESEND, e_new, NULL, m, &ea, sub);
1589 ARRAY_FREE(&ea);
1590
1591 return rc;
1592}
1593
1601static bool is_reply(struct Email *reply, struct Email *orig)
1602{
1603 if (!reply || !reply->env || !orig || !orig->env)
1604 return false;
1605 return mutt_list_find(&orig->env->references, reply->env->message_id) ||
1606 mutt_list_find(&orig->env->in_reply_to, reply->env->message_id);
1607}
1608
1620static bool search_attach_keyword(char *filename, struct ConfigSubset *sub)
1621{
1622 const struct Regex *c_abort_noattach_regex = cs_subset_regex(sub, "abort_noattach_regex");
1623 const struct Regex *c_quote_regex = cs_subset_regex(sub, "quote_regex");
1624
1625 /* Search for the regex in `$abort_noattach_regex` within a file */
1626 if (!c_abort_noattach_regex || !c_abort_noattach_regex->regex ||
1627 !c_quote_regex || !c_quote_regex->regex)
1628 {
1629 return false;
1630 }
1631
1632 FILE *fp_att = mutt_file_fopen(filename, "r");
1633 if (!fp_att)
1634 return false;
1635
1636 char *inputline = MUTT_MEM_MALLOC(1024, char);
1637 bool found = false;
1638 while (!feof(fp_att) && fgets(inputline, 1024, fp_att))
1639 {
1640 if (!mutt_is_quote_line(inputline, NULL) &&
1641 mutt_regex_match(c_abort_noattach_regex, inputline))
1642 {
1643 found = true;
1644 break;
1645 }
1646 }
1647 FREE(&inputline);
1648 mutt_file_fclose(&fp_att);
1649 return found;
1650}
1651
1665static int save_fcc(struct Mailbox *m, struct Email *e, struct Buffer *fcc,
1666 struct Body *clear_content, char *pgpkeylist,
1667 SendFlags flags, char **finalpath, struct ConfigSubset *sub)
1668{
1669 int rc = 0;
1670 struct Body *save_content = NULL;
1671
1672 expand_path(fcc, false);
1673
1674 /* Don't save a copy when we are in batch-mode, and the FCC
1675 * folder is on an IMAP server: This would involve possibly lots
1676 * of user interaction, which is not available in batch mode.
1677 *
1678 * Note: A patch to fix the problems with the use of IMAP servers
1679 * from non-curses mode is available from Brendan Cully. However,
1680 * I'd like to think a bit more about this before including it. */
1681
1682 if ((flags & SEND_BATCH) && !buf_is_empty(fcc) &&
1683 (imap_path_probe(buf_string(fcc), NULL) == MUTT_IMAP))
1684 {
1685 mutt_error(_("Warning: Fcc to an IMAP mailbox is not supported in batch mode"));
1686 /* L10N: Printed after the "Fcc to an IMAP mailbox is not supported" message.
1687 To make it clearer that the message doesn't mean NeoMutt is aborting
1688 sending the mail too.
1689 %s is the full mailbox URL, including imap(s)://
1690 */
1691 mutt_error(_("Skipping Fcc to %s"), buf_string(fcc));
1692 buf_reset(fcc);
1693 return rc;
1694 }
1695
1696 if (buf_is_empty(fcc) || mutt_str_equal("/dev/null", buf_string(fcc)))
1697 return rc;
1698
1699 struct Body *tmpbody = e->body;
1700 struct Body *save_sig = NULL;
1701 struct Body *save_parts = NULL;
1702
1703 const bool c_fcc_before_send = cs_subset_bool(sub, "fcc_before_send");
1704 /* Before sending, we don't allow message manipulation because it
1705 * will break message signatures. This is especially complicated by
1706 * Protected Headers. */
1707 if (!c_fcc_before_send)
1708 {
1709 const bool c_fcc_clear = cs_subset_bool(sub, "fcc_clear");
1710 if ((WithCrypto != 0) &&
1711 (e->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT)) && c_fcc_clear)
1712 {
1713 e->body = clear_content;
1716 mutt_param_delete(&e->body->parameter, "protected-headers");
1717 }
1718
1719 const enum QuadOption c_fcc_attach = cs_subset_quad(sub, "fcc_attach");
1720
1721 /* check to see if the user wants copies of all attachments */
1722 bool save_atts = true;
1723 if (e->body->type == TYPE_MULTIPART)
1724 {
1725 /* In batch mode, save attachments if the quadoption is yes or ask-yes */
1726 if (flags & SEND_BATCH)
1727 {
1728 if ((c_fcc_attach == MUTT_NO) || (c_fcc_attach == MUTT_ASKNO))
1729 save_atts = false;
1730 }
1731 else if (query_quadoption(_("Save attachments in Fcc?"), sub, "fcc_attach") != MUTT_YES)
1732 {
1733 save_atts = false;
1734 }
1735 }
1736 if (!save_atts)
1737 {
1738 if ((WithCrypto != 0) && (e->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT)) &&
1739 (mutt_str_equal(e->body->subtype, "encrypted") ||
1740 mutt_str_equal(e->body->subtype, "signed")))
1741 {
1742 if ((clear_content->type == TYPE_MULTIPART) &&
1743 (query_quadoption(_("Save attachments in Fcc?"), sub, "fcc_attach") != MUTT_YES))
1744 {
1745 if (!(e->security & SEC_ENCRYPT) && (e->security & SEC_SIGN))
1746 {
1747 /* save initial signature and attachments */
1748 save_sig = e->body->parts->next;
1749 save_parts = clear_content->parts->next;
1750 }
1751
1752 /* this means writing only the main part */
1753 e->body = clear_content->parts;
1754
1755 if (mutt_protect(e, pgpkeylist, false) == -1)
1756 {
1757 /* we can't do much about it at this point, so
1758 * fallback to saving the whole thing to fcc */
1759 e->body = tmpbody;
1760 save_sig = NULL;
1761 goto full_fcc;
1762 }
1763
1764 save_content = e->body;
1765 }
1766 }
1767 else
1768 {
1769 if (query_quadoption(_("Save attachments in Fcc?"), sub, "fcc_attach") != MUTT_YES)
1770 e->body = e->body->parts;
1771 }
1772 }
1773 }
1774
1775full_fcc:
1776 if (e->body)
1777 {
1778 /* update received time so that when storing to a mbox-style folder
1779 * the From_ line contains the current time instead of when the
1780 * message was first postponed. */
1781 e->received = mutt_date_now();
1782 rc = mutt_write_multiple_fcc(buf_string(fcc), e, NULL, false, NULL, finalpath, sub);
1783 while (rc && !(flags & SEND_BATCH))
1784 {
1786 int choice = mw_multi_choice(
1787 /* L10N: Called when saving to $record or Fcc failed after sending.
1788 (r)etry tries the same mailbox again.
1789 alternate (m)ailbox prompts for a different mailbox to try.
1790 (s)kip aborts saving. */
1791 _("Fcc failed. (r)etry, alternate (m)ailbox, or (s)kip?"),
1792 /* L10N: These correspond to the "Fcc failed" multi-choice prompt
1793 (r)etry, alternate (m)ailbox, or (s)kip.
1794 Any similarity to famous leaders of the FSF is coincidental. */
1795 _("rms"));
1796 switch (choice)
1797 {
1798 case 2: /* alternate (m)ailbox */
1799 /* L10N: This is the prompt to enter an "alternate (m)ailbox" when the
1800 initial Fcc fails. */
1801 rc = mw_enter_fname(_("Fcc mailbox"), fcc, true, m, false, NULL, NULL,
1803 if ((rc == -1) || buf_is_empty(fcc))
1804 {
1805 rc = 0;
1806 break;
1807 }
1809
1810 case 1: /* (r)etry */
1811 rc = mutt_write_multiple_fcc(buf_string(fcc), e, NULL, false, NULL, finalpath, sub);
1812 break;
1813
1814 case -1: /* abort */
1815 case 3: /* (s)kip */
1816 rc = 0;
1817 break;
1818 }
1819 }
1820 }
1821
1822 if (!c_fcc_before_send)
1823 {
1824 e->body = tmpbody;
1825
1826 if ((WithCrypto != 0) && save_sig)
1827 {
1828 /* cleanup the second signature structures */
1829 if (save_content->parts)
1830 {
1831 mutt_body_free(&save_content->parts->next);
1832 save_content->parts = NULL;
1833 }
1834 mutt_body_free(&save_content);
1835
1836 /* restore old signature and attachments */
1837 e->body->parts->next = save_sig;
1838 e->body->parts->parts->next = save_parts;
1839 }
1840 else if ((WithCrypto != 0) && save_content)
1841 {
1842 /* destroy the new encrypted body. */
1843 mutt_body_free(&save_content);
1844 }
1845 }
1846
1847 return 0;
1848}
1849
1860static int postpone_message(struct Email *e_post, struct Email *e_cur,
1861 const char *fcc, SendFlags flags, struct ConfigSubset *sub)
1862{
1863 char *pgpkeylist = NULL;
1864 const char *encrypt_as = NULL;
1865 struct Body *clear_content = NULL;
1866
1867 const char *const c_postponed = cs_subset_string(sub, "postponed");
1868 if (!c_postponed)
1869 {
1870 mutt_error(_("Can't postpone. $postponed is unset"));
1871 return -1;
1872 }
1873
1874 if (e_post->body->next)
1875 e_post->body = mutt_make_multipart(e_post->body);
1876
1877 mutt_encode_descriptions(e_post->body, true, sub);
1878
1879 const bool c_postpone_encrypt = cs_subset_bool(sub, "postpone_encrypt");
1880 if ((WithCrypto != 0) && c_postpone_encrypt &&
1881 (e_post->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)))
1882 {
1883 if (((WithCrypto & APPLICATION_PGP) != 0) && (e_post->security & APPLICATION_PGP))
1884 {
1885 const char *const c_pgp_default_key = cs_subset_string(sub, "pgp_default_key");
1886 encrypt_as = c_pgp_default_key;
1887 }
1888 else if (((WithCrypto & APPLICATION_SMIME) != 0) && (e_post->security & APPLICATION_SMIME))
1889 {
1890 const char *const c_smime_default_key = cs_subset_string(sub, "smime_default_key");
1891 encrypt_as = c_smime_default_key;
1892 }
1893 if (!encrypt_as)
1894 {
1895 const char *const c_postpone_encrypt_as = cs_subset_string(sub, "postpone_encrypt_as");
1896 encrypt_as = c_postpone_encrypt_as;
1897 }
1898
1899#ifdef USE_AUTOCRYPT
1900 if (e_post->security & SEC_AUTOCRYPT)
1901 {
1903 {
1904 if (mutt_istr_equal(e_post->body->subtype, "mixed"))
1905 e_post->body = mutt_remove_multipart(e_post->body);
1906 decode_descriptions(e_post->body);
1907 mutt_error(_("Error encrypting message. Check your crypt settings."));
1908 return -1;
1909 }
1910 encrypt_as = AutocryptDefaultKey;
1911 }
1912#endif
1913
1914 if (encrypt_as)
1915 {
1916 pgpkeylist = mutt_str_dup(encrypt_as);
1917 clear_content = e_post->body;
1918 if (mutt_protect(e_post, pgpkeylist, true) == -1)
1919 {
1920 FREE(&pgpkeylist);
1921 if (mutt_istr_equal(e_post->body->subtype, "mixed"))
1922 e_post->body = mutt_remove_multipart(e_post->body);
1923 decode_descriptions(e_post->body);
1924 mutt_error(_("Error encrypting message. Check your crypt settings."));
1925 return -1;
1926 }
1927
1928 FREE(&pgpkeylist);
1929
1930 mutt_encode_descriptions(e_post->body, false, sub);
1931 }
1932 }
1933
1934 /* make sure the message is written to the right part of a maildir
1935 * postponed folder. */
1936 e_post->read = false;
1937 e_post->old = false;
1938
1939 mutt_prepare_envelope(e_post->env, false, sub);
1940 mutt_env_to_intl(e_post->env, NULL, NULL); /* Handle bad IDNAs the next time. */
1941
1942 if (mutt_write_fcc(NONULL(c_postponed), e_post,
1943 (e_cur && (flags & SEND_REPLY)) ? e_cur->env->message_id : NULL,
1944 true, fcc, NULL, sub) < 0)
1945 {
1946 if (clear_content)
1947 {
1948 mutt_body_free(&e_post->body);
1949 e_post->body = clear_content;
1950 }
1951 mutt_env_free(&e_post->body->mime_headers); /* protected headers */
1952 mutt_param_delete(&e_post->body->parameter, "protected-headers");
1953 if (mutt_istr_equal(e_post->body->subtype, "mixed"))
1954 e_post->body = mutt_remove_multipart(e_post->body);
1955 decode_descriptions(e_post->body);
1957 return -1;
1958 }
1959
1961
1962 if (clear_content)
1963 mutt_body_free(&clear_content);
1964
1965 return 0;
1966}
1967
1974static bool is_text_plain(const struct Body *b)
1975{
1976 return (b->type == TYPE_TEXT) && mutt_istr_equal(b->subtype, "plain");
1977}
1978
1985static bool abort_for_missing_attachments(const struct Body *b, struct ConfigSubset *sub)
1986{
1987 const enum QuadOption c_abort_noattach = cs_subset_quad(sub, "abort_noattach");
1988
1989 if (c_abort_noattach == MUTT_NO)
1990 return false;
1991
1992 if (b->next)
1993 return false;
1994
1995 bool has_keyword = false;
1996
1997 /* search text/plain parts, whether they are main or alternative parts */
1998 if (is_text_plain(b))
1999 {
2000 has_keyword |= search_attach_keyword(b->filename, sub);
2001 }
2002 else
2003 {
2004 for (b = b->parts; b; b = b->next)
2005 {
2006 if (is_text_plain(b))
2007 {
2008 has_keyword |= search_attach_keyword(b->filename, sub);
2009 }
2010 }
2011 }
2012
2013 if (!has_keyword)
2014 return false;
2015
2016 if (c_abort_noattach == MUTT_YES)
2017 {
2018 mutt_error(_("Message contains text matching \"$abort_noattach_regex\". Not sending."));
2019 return true;
2020 }
2021
2022 return query_quadoption(_("No attachments, cancel sending?"), sub, "abort_noattach") != MUTT_NO;
2023}
2024
2037int mutt_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile,
2038 struct Mailbox *m, struct EmailArray *ea, struct ConfigSubset *sub)
2039{
2040 struct Buffer *fcc = buf_pool_get(); /* where to copy this message */
2041 FILE *fp_tmp = NULL;
2042 struct Body *pbody = NULL;
2043 int i;
2044 bool free_clear_content = false;
2045
2046 struct Body *clear_content = NULL;
2047 char *pgpkeylist = NULL;
2048 /* save current value of "pgp_sign_as" and "smime_default_key" */
2049 char *pgp_sign_as = NULL;
2050 char *smime_sign_as = NULL;
2051 const char *tag = NULL;
2052 char *err = NULL;
2053 const char *ctype = NULL;
2054 char *finalpath = NULL;
2055 struct Email *e_cur = NULL;
2056
2057 if (ea && (ARRAY_SIZE(ea) == 1))
2058 e_cur = *ARRAY_GET(ea, 0);
2059
2060 int rc = -1;
2061
2062 if (flags & SEND_NEWS)
2063 OptNewsSend = true;
2064 else
2065 OptNewsSend = false;
2066
2067 const enum QuadOption c_recall = cs_subset_quad(sub, "recall");
2068
2069 if (!flags && !e_templ && (c_recall != MUTT_NO) && mutt_num_postponed(m, true))
2070 {
2071 /* If the user is composing a new message, check to see if there
2072 * are any postponed messages first. */
2073 enum QuadOption ans = query_quadoption(_("Recall postponed message?"), sub, "recall");
2074 if (ans == MUTT_ABORT)
2075 return rc;
2076
2077 if (ans == MUTT_YES)
2078 flags |= SEND_POSTPONED;
2079 }
2080
2081 if (flags & SEND_POSTPONED)
2082 {
2084 {
2085 const char *const c_pgp_sign_as = cs_subset_string(sub, "pgp_sign_as");
2086 pgp_sign_as = mutt_str_dup(c_pgp_sign_as);
2087 }
2089 {
2090 const char *const c_smime_sign_as = cs_subset_string(sub, "smime_sign_as");
2091 smime_sign_as = mutt_str_dup(c_smime_sign_as);
2092 }
2093 }
2094
2095 /* Delay expansion of aliases until absolutely necessary--shouldn't
2096 * be necessary unless we are prompting the user or about to execute a
2097 * send-hook. */
2098
2099 if (!e_templ)
2100 {
2101 e_templ = email_new();
2102
2103 if (flags == SEND_POSTPONED)
2104 {
2105 rc = mutt_get_postponed(m, e_templ, &e_cur, fcc);
2106 if (rc < 0)
2107 {
2108 flags = SEND_POSTPONED;
2109 goto cleanup;
2110 }
2111 flags = rc;
2112 /* If postponed message is a news article, it have
2113 * a "Newsgroups:" header line, then set appropriate flag. */
2114 if (e_templ->env->newsgroups)
2115 {
2116 flags |= SEND_NEWS;
2117 OptNewsSend = true;
2118 }
2119 else
2120 {
2121 flags &= ~SEND_NEWS;
2122 OptNewsSend = false;
2123 }
2124 }
2125
2126 if (flags & (SEND_POSTPONED | SEND_RESEND))
2127 {
2128 struct Body *b = e_templ->body;
2129 while (b->parts)
2130 b = b->parts;
2131 fp_tmp = mutt_file_fopen(b->filename, "a+");
2132 if (!fp_tmp)
2133 {
2134 mutt_perror("%s", b->filename);
2135 goto cleanup;
2136 }
2137 }
2138
2139 if (!e_templ->env)
2140 e_templ->env = mutt_env_new();
2141 }
2142
2143 /* Parse and use an eventual list-post header */
2144 if ((flags & SEND_LIST_REPLY) && e_cur && e_cur->env && e_cur->env->list_post)
2145 {
2146 /* Use any list-post header as a template */
2147 mutt_parse_mailto(e_templ->env, NULL, e_cur->env->list_post);
2148 /* We don't let them set the sender's address. */
2149 mutt_addrlist_clear(&e_templ->env->from);
2150 }
2151
2152 if (!(flags & (SEND_KEY | SEND_POSTPONED | SEND_RESEND)))
2153 {
2154 /* When SEND_DRAFT_FILE is set, the caller has already
2155 * created the "parent" body structure. */
2156 if (!(flags & SEND_DRAFT_FILE))
2157 {
2158 pbody = mutt_body_new();
2159 pbody->next = e_templ->body; /* don't kill command-line attachments */
2160 e_templ->body = pbody;
2161
2162 const char *const c_content_type = cs_subset_string(sub, "content_type");
2163 ctype = c_content_type;
2164 if (!ctype)
2165 ctype = "text/plain";
2166 mutt_parse_content_type(ctype, e_templ->body);
2167 e_templ->body->unlink = true;
2168 e_templ->body->use_disp = false;
2169 e_templ->body->disposition = DISP_INLINE;
2170
2171 if (tempfile)
2172 {
2173 fp_tmp = mutt_file_fopen(tempfile, "a+");
2174 e_templ->body->filename = mutt_str_dup(tempfile);
2175 if (flags & SEND_NO_FREE_HEADER)
2176 e_templ->body->unlink = false;
2177 }
2178 else
2179 {
2180 struct Buffer *buf = buf_pool_get();
2181 buf_mktemp(buf);
2182 fp_tmp = mutt_file_fopen(buf_string(buf), "w+");
2183 e_templ->body->filename = buf_strdup(buf);
2184 buf_pool_release(&buf);
2185 }
2186 }
2187 else
2188 {
2189 struct Body *b = e_templ->body;
2190 while (b->parts)
2191 b = b->parts;
2192 fp_tmp = mutt_file_fopen(b->filename, "a+");
2193 }
2194
2195 if (!fp_tmp)
2196 {
2197 mutt_debug(LL_DEBUG1, "can't create tempfile %s (errno=%d)\n",
2198 e_templ->body->filename, errno);
2199 mutt_perror("%s", e_templ->body->filename);
2200 goto cleanup;
2201 }
2202 }
2203
2204 const bool c_reverse_name = cs_subset_bool(sub, "reverse_name");
2205 /* this is handled here so that the user can match ~f in send-hook */
2206 if (e_cur && c_reverse_name && !(flags & (SEND_POSTPONED | SEND_RESEND)))
2207 {
2208 /* We shouldn't have to worry about alias expansion here since we are
2209 * either replying to a real or postponed message, therefore no aliases
2210 * should exist since the user has not had the opportunity to add
2211 * addresses to the list. We just have to ensure the postponed messages
2212 * have their aliases expanded. */
2213
2214 if (!TAILQ_EMPTY(&e_templ->env->from))
2215 {
2216 mutt_debug(LL_DEBUG5, "e_templ->env->from before set_reverse_name: %s\n",
2217 buf_string(TAILQ_FIRST(&e_templ->env->from)->mailbox));
2218 mutt_addrlist_clear(&e_templ->env->from);
2219 }
2220 set_reverse_name(&e_templ->env->from, e_cur->env, sub);
2221 }
2222
2223 const bool c_reply_with_xorig = cs_subset_bool(sub, "reply_with_xorig");
2224 if (e_cur && c_reply_with_xorig && !(flags & (SEND_POSTPONED | SEND_RESEND | SEND_FORWARD)))
2225 {
2226 /* We shouldn't have to worry about freeing 'e_templ->env->from' before
2227 * setting it here since this code will only execute when doing some
2228 * sort of reply. The pointer will only be set when using the -H command
2229 * line option.
2230 *
2231 * If there is already a from address recorded in 'e_templ->env->from',
2232 * then it theoretically comes from `$reverse_name` handling, and we don't use
2233 * the 'X-Original-To header'. */
2234 if (!TAILQ_EMPTY(&e_cur->env->x_original_to) && TAILQ_EMPTY(&e_templ->env->from))
2235 {
2236 mutt_addrlist_copy(&e_templ->env->from, &e_cur->env->x_original_to, false);
2237 mutt_debug(LL_DEBUG5, "e_templ->env->from extracted from X-Original-To: header: %s\n",
2238 buf_string(TAILQ_FIRST(&e_templ->env->from)->mailbox));
2239 }
2240 }
2241
2242 if (!e_templ->env->message_id)
2243 e_templ->env->message_id = msgid_generate();
2244
2245 const bool c_resume_draft_files = cs_subset_bool(sub, "resume_draft_files");
2246 if (!(flags & (SEND_POSTPONED | SEND_RESEND)) &&
2247 !((flags & SEND_DRAFT_FILE) && c_resume_draft_files))
2248 {
2249 if ((flags & (SEND_REPLY | SEND_FORWARD | SEND_TO_SENDER)) &&
2250 (envelope_defaults(e_templ->env, ea, flags, sub) == -1))
2251 {
2252 goto cleanup;
2253 }
2254
2255 const bool c_hdrs = cs_subset_bool(sub, "hdrs");
2256 if (c_hdrs)
2257 process_user_recips(e_templ->env);
2258
2259 /* Expand aliases and remove duplicates/crossrefs */
2260 mutt_expand_aliases_env(e_templ->env);
2261
2262 if (flags & SEND_REPLY)
2263 mutt_fix_reply_recipients(e_templ->env, sub);
2264
2265 if ((flags & SEND_NEWS) && (m && m->type == MUTT_NNTP) && !e_templ->env->newsgroups)
2266 {
2267 e_templ->env->newsgroups = mutt_str_dup(((struct NntpMboxData *) m->mdata)->group);
2268 }
2269
2270 const bool c_auto_edit = cs_subset_bool(sub, "auto_edit");
2271 const bool c_edit_headers = cs_subset_bool(sub, "edit_headers");
2272 const bool c_fast_reply = cs_subset_bool(sub, "fast_reply");
2273 if (!(flags & SEND_BATCH) && !(c_auto_edit && c_edit_headers) &&
2274 !((flags & SEND_REPLY) && c_fast_reply))
2275 {
2276 if (edit_envelope(e_templ->env, flags, sub) == -1)
2277 goto cleanup;
2278 }
2279
2280 /* the from address must be set here regardless of whether or not
2281 * $use_from is set so that the '~P' (from you) operator in send-hook
2282 * patterns will work. if $use_from is unset, the from address is killed
2283 * after send-hooks are evaluated */
2284
2285 const bool killfrom = TAILQ_EMPTY(&e_templ->env->from);
2286 if (killfrom)
2287 {
2289 }
2290
2291 if ((flags & SEND_REPLY) && e_cur)
2292 {
2293 /* change setting based upon message we are replying to */
2295
2296 /* set the replied flag for the message we are generating so that the
2297 * user can use ~Q in a send-hook to know when reply-hook's are also
2298 * being used. */
2299 e_templ->replied = true;
2300 }
2301
2302 /* change settings based upon recipients */
2303
2304 exec_message_hook(NULL, e_templ, CMD_SEND_HOOK);
2305
2306 /* Unset the replied flag from the message we are composing since it is
2307 * no longer required. This is done here because the FCC'd copy of
2308 * this message was erroneously get the 'R'eplied flag when stored in
2309 * a maildir-style mailbox. */
2310 e_templ->replied = false;
2311
2312 /* $use_from and/or $from might have changed in a send-hook */
2313 if (killfrom)
2314 {
2315 mutt_addrlist_clear(&e_templ->env->from);
2316
2317 const bool c_use_from = cs_subset_bool(sub, "use_from");
2318 if (c_use_from && !(flags & (SEND_POSTPONED | SEND_RESEND)))
2320 }
2321
2322 if (c_hdrs)
2323 process_user_header(e_templ->env);
2324
2325 if ((flags & SEND_BATCH) && !(flags & SEND_CONSUMED_STDIN))
2326 {
2327 if (mutt_file_copy_stream(stdin, fp_tmp) < 0)
2328 {
2329 mutt_error(_("Error sending message"));
2330 goto cleanup;
2331 }
2332 }
2333
2334 if (!(flags & SEND_BATCH))
2335 mutt_make_greeting(e_templ, fp_tmp, sub);
2336
2337 const bool c_sig_on_top = cs_subset_bool(sub, "sig_on_top");
2338 const char *const c_editor = cs_subset_string(sub, "editor");
2339 if (c_sig_on_top && !(flags & (SEND_KEY | SEND_BATCH)) && c_editor)
2340 {
2341 append_signature(fp_tmp, sub);
2342 }
2343
2344 /* include replies/forwarded messages, unless we are given a template */
2345 if (!tempfile && (m || !(flags & (SEND_REPLY | SEND_FORWARD))) &&
2346 (generate_body(fp_tmp, e_templ, flags, m, ea, sub) == -1))
2347 {
2348 goto cleanup;
2349 }
2350
2351 if (!c_sig_on_top && !(flags & (SEND_KEY | SEND_BATCH)) && c_editor)
2352 {
2353 append_signature(fp_tmp, sub);
2354 }
2355 }
2356
2357 /* Only set format=flowed for new messages. postponed/resent/draftfiles
2358 * should respect the original email.
2359 *
2360 * This is set here so that send-hook can be used to turn the option on. */
2361 if (!(flags & (SEND_KEY | SEND_POSTPONED | SEND_RESEND | SEND_DRAFT_FILE)))
2362 {
2363 const bool c_text_flowed = cs_subset_bool(sub, "text_flowed");
2364 if (c_text_flowed && is_text_plain(e_templ->body))
2365 {
2366 mutt_param_set(&e_templ->body->parameter, "format", "flowed");
2367 }
2368 }
2369
2370 /* This hook is even called for postponed messages, and can, e.g., be used
2371 * for setting the editor, the sendmail path, or the envelope sender. */
2372 exec_message_hook(NULL, e_templ, CMD_SEND2_HOOK);
2373
2374 /* wait until now to set the real name portion of our return address so
2375 * that $real_name can be set in a send-hook */
2376 {
2377 struct Address *from = TAILQ_FIRST(&e_templ->env->from);
2378 if (from && !from->personal && !(flags & (SEND_RESEND | SEND_POSTPONED)))
2379 {
2380 const char *const c_real_name = cs_subset_string(sub, "real_name");
2381 if (c_real_name)
2382 from->personal = buf_new(c_real_name);
2383 }
2384 }
2385
2386 if (!(((WithCrypto & APPLICATION_PGP) != 0) && (flags & SEND_KEY)))
2387 mutt_file_fclose(&fp_tmp);
2388
2389 if (!(flags & SEND_BATCH))
2390 {
2391 struct stat st = { 0 };
2392 time_t mtime;
2393 struct Body *b = e_templ->body;
2394 while (b->parts)
2395 b = b->parts;
2396 mtime = mutt_file_decrease_mtime(b->filename, NULL);
2397 if (mtime == (time_t) -1)
2398 {
2399 mutt_perror("%s", b->filename);
2400 goto cleanup;
2401 }
2402
2403 mutt_update_encoding(b, sub);
2404
2405 const bool c_edit_headers = cs_subset_bool(sub, "edit_headers");
2406 const bool c_auto_edit = cs_subset_bool(sub, "auto_edit");
2407
2408 /* Select whether or not the user's editor should be called now. We
2409 * don't want to do this when:
2410 * 1) we are sending a key/cert
2411 * 2) we are forwarding a message and the user doesn't want to edit it.
2412 * This is controlled by the quadoption $forward_edit. However, if
2413 * both $edit_headers and $auto_edit are set, we want to ignore the
2414 * setting of $forward_edit because the user probably needs to add the
2415 * recipients. */
2416 if (!(flags & SEND_KEY) &&
2417 (((flags & SEND_FORWARD) == 0) || (c_edit_headers && c_auto_edit) ||
2418 (query_quadoption(_("Edit forwarded message?"), sub, "forward_edit") == MUTT_YES)))
2419 {
2420 /* If the this isn't a text message, look for a mailcap edit command */
2421 const char *const c_editor = cs_subset_string(sub, "editor");
2422 b = e_templ->body;
2423 while (b->parts)
2424 b = b->parts;
2425 if (mutt_needs_mailcap(b))
2426 {
2427 if (!mutt_edit_attachment(b))
2428 goto cleanup;
2429 }
2430 else if (c_edit_headers)
2431 {
2432 mutt_env_to_local(e_templ->env);
2433 mutt_edit_headers(c_editor, b->filename, e_templ, fcc);
2434 mutt_env_to_intl(e_templ->env, NULL, NULL);
2435 }
2436 else
2437 {
2438 mutt_edit_file(c_editor, b->filename);
2439 if (stat(b->filename, &st) == 0)
2440 {
2441 if (mtime != st.st_mtime)
2443 }
2444 else
2445 {
2446 mutt_perror("%s", b->filename);
2447 }
2448 }
2449
2450 exec_message_hook(NULL, e_templ, CMD_SEND2_HOOK);
2451 }
2452
2454 {
2455 if (stat(e_templ->body->filename, &st) == 0)
2456 {
2457 /* if the file was not modified, bail out now */
2458 if ((mtime == st.st_mtime) && !e_templ->body->next &&
2459 (query_quadoption(_("Abort unmodified message?"), sub, "abort_unmodified") == MUTT_YES))
2460 {
2461 mutt_message(_("Aborted unmodified message"));
2462 goto cleanup;
2463 }
2464 }
2465 else
2466 {
2467 mutt_perror("%s", e_templ->body->filename);
2468 }
2469 }
2470 }
2471
2472 /* Set the message security unless:
2473 * 1) crypto support is not enabled (WithCrypto==0)
2474 * 2) pgp: header field was present during message editing with $edit_headers (e_templ->security != 0)
2475 * 3) we are resending a message
2476 * 4) we are recalling a postponed message (don't override the user's saved settings)
2477 * 5) we are in batch mode
2478 * But 3, 4, and 5, can be overridden with '-C' in the command line (flags & SEND_CLI_CRYPTO)
2479 *
2480 * This is done after allowing the user to edit the message so that security
2481 * settings can be configured with send2-hook and $edit_headers. */
2482 if ((WithCrypto != 0) && (e_templ->security == 0) &&
2483 ((flags & SEND_CLI_CRYPTO) || !(flags & (SEND_BATCH | SEND_POSTPONED | SEND_RESEND))))
2484 {
2485 bool c_autocrypt = false;
2486 bool c_autocrypt_reply = false;
2487
2488#ifdef USE_AUTOCRYPT
2489 c_autocrypt = cs_subset_bool(sub, "autocrypt");
2490 c_autocrypt_reply = cs_subset_bool(sub, "autocrypt_reply");
2491#endif
2492
2493 if (c_autocrypt && c_autocrypt_reply && e_cur && (e_cur->security & SEC_AUTOCRYPT))
2494 {
2496 }
2497 else
2498 {
2499 const bool c_crypt_auto_sign = cs_subset_bool(sub, "crypt_auto_sign");
2500 const bool c_crypt_auto_encrypt = cs_subset_bool(sub, "crypt_auto_encrypt");
2501 const bool c_crypt_reply_encrypt = cs_subset_bool(sub, "crypt_reply_encrypt");
2502 const bool c_crypt_reply_sign = cs_subset_bool(sub, "crypt_reply_sign");
2503 const bool c_crypt_reply_sign_encrypted = cs_subset_bool(sub, "crypt_reply_sign_encrypted");
2504
2505 if (c_crypt_auto_sign)
2506 e_templ->security |= SEC_SIGN;
2507 if (c_crypt_auto_encrypt)
2508 e_templ->security |= SEC_ENCRYPT;
2509 if (c_crypt_reply_encrypt && e_cur && (e_cur->security & SEC_ENCRYPT))
2510 e_templ->security |= SEC_ENCRYPT;
2511 if (c_crypt_reply_sign && e_cur && (e_cur->security & SEC_SIGN))
2512 e_templ->security |= SEC_SIGN;
2513 if (c_crypt_reply_sign_encrypted && e_cur && (e_cur->security & SEC_ENCRYPT))
2514 e_templ->security |= SEC_SIGN;
2515
2516 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(sub, "crypt_opportunistic_encrypt");
2517
2518 if (((WithCrypto & APPLICATION_PGP) != 0) &&
2519 ((e_templ->security & (SEC_ENCRYPT | SEC_SIGN)) || c_crypt_opportunistic_encrypt))
2520 {
2521 const bool c_pgp_auto_inline = cs_subset_bool(sub, "pgp_auto_inline");
2522 const bool c_pgp_reply_inline = cs_subset_bool(sub, "pgp_reply_inline");
2523
2524 if (c_pgp_auto_inline)
2525 e_templ->security |= SEC_INLINE;
2526 if (c_pgp_reply_inline && e_cur && (e_cur->security & SEC_INLINE))
2527 e_templ->security |= SEC_INLINE;
2528 }
2529 }
2530
2531 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(sub, "crypt_opportunistic_encrypt");
2532
2533 if (e_templ->security || c_crypt_opportunistic_encrypt)
2534 {
2535 const bool c_crypt_auto_pgp = cs_subset_bool(sub, "crypt_auto_pgp");
2536 const bool c_crypt_auto_smime = cs_subset_bool(sub, "crypt_auto_smime");
2537
2538 /* When replying / forwarding, use the original message's
2539 * crypto system. According to the documentation,
2540 * smime_is_default should be disregarded here.
2541 *
2542 * Problem: At least with forwarding, this doesn't really
2543 * make much sense. Should we have an option to completely
2544 * disable individual mechanisms at run-time? */
2545 if (e_cur)
2546 {
2547 if (((WithCrypto & APPLICATION_PGP) != 0) && c_crypt_auto_pgp &&
2548 (e_cur->security & APPLICATION_PGP))
2549 {
2550 e_templ->security |= APPLICATION_PGP;
2551 }
2552 else if (((WithCrypto & APPLICATION_SMIME) != 0) &&
2553 c_crypt_auto_smime && (e_cur->security & APPLICATION_SMIME))
2554 {
2555 e_templ->security |= APPLICATION_SMIME;
2556 }
2557 }
2558
2559 const bool c_smime_is_default = cs_subset_bool(sub, "smime_is_default");
2560
2561 /* No crypto mechanism selected? Use availability + smime_is_default
2562 * for the decision. */
2563 if (!(e_templ->security & (APPLICATION_SMIME | APPLICATION_PGP)))
2564 {
2565 if (((WithCrypto & APPLICATION_SMIME) != 0) && c_crypt_auto_smime && c_smime_is_default)
2566 {
2567 e_templ->security |= APPLICATION_SMIME;
2568 }
2569 else if (((WithCrypto & APPLICATION_PGP) != 0) && c_crypt_auto_pgp)
2570 {
2571 e_templ->security |= APPLICATION_PGP;
2572 }
2573 else if (((WithCrypto & APPLICATION_SMIME) != 0) && c_crypt_auto_smime)
2574 {
2575 e_templ->security |= APPLICATION_SMIME;
2576 }
2577 }
2578 }
2579
2580 /* opportunistic encrypt relies on SMIME or PGP already being selected */
2581 if (c_crypt_opportunistic_encrypt)
2582 {
2583 /* If something has already enabled encryption, e.g. `$crypt_auto_encrypt`
2584 * or `$crypt_reply_encrypt`, then don't enable opportunistic encrypt for
2585 * the message. */
2586 if (!(e_templ->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)))
2587 {
2588 e_templ->security |= SEC_OPPENCRYPT;
2590 }
2591 }
2592
2593 /* No permissible mechanisms found. Don't sign or encrypt. */
2594 if (!(e_templ->security & (APPLICATION_SMIME | APPLICATION_PGP)))
2595 e_templ->security = SEC_NO_FLAGS;
2596 }
2597
2598 /* Deal with the corner case where the crypto module backend is not available.
2599 * This can happen if configured without PGP/SMIME and with GPGME, but
2600 * $crypt_use_gpgme is unset. */
2601 if (e_templ->security && !crypt_has_module_backend(e_templ->security))
2602 {
2603 mutt_error(_("No crypto backend configured. Disabling message security setting."));
2604 e_templ->security = SEC_NO_FLAGS;
2605 }
2606
2607 /* specify a default fcc. if we are in batchmode, only save a copy of
2608 * the message if the value of $copy is yes or ask-yes */
2609
2610 const enum QuadOption c_copy = cs_subset_quad(sub, "copy");
2611
2612 if (buf_is_empty(fcc) && !(flags & SEND_POSTPONED_FCC) &&
2613 (!(flags & SEND_BATCH) || (c_copy & 0x1)))
2614 {
2615 /* set the default FCC */
2616 const bool killfrom = TAILQ_EMPTY(&e_templ->env->from);
2617 if (killfrom)
2618 {
2620 }
2621 mutt_select_fcc(fcc, e_templ);
2622 if (killfrom)
2623 {
2624 mutt_addrlist_clear(&e_templ->env->from);
2625 }
2626 }
2627
2628 mutt_rfc3676_space_stuff(e_templ);
2629
2630 mutt_update_encoding(e_templ->body, sub);
2631
2632 if (!(flags & SEND_BATCH))
2633 {
2634 main_loop:
2635
2636 pretty_mailbox(fcc);
2637 i = dlg_compose(e_templ, fcc,
2638 ((flags & SEND_NO_FREE_HEADER) ? MUTT_COMPOSE_NOFREEHEADER : 0), sub);
2639 if (i == -1)
2640 {
2641 /* abort */
2642 if (flags & SEND_NEWS)
2643 mutt_message(_("Article not posted"));
2644 else
2645 mutt_message(_("Mail not sent"));
2646 goto cleanup;
2647 }
2648 else if (i == 1)
2649 {
2650 if (postpone_message(e_templ, e_cur, buf_string(fcc), flags, sub) != 0)
2651 goto main_loop;
2652 mutt_message(_("Message postponed"));
2653 rc = 1;
2654 goto cleanup;
2655 }
2656 }
2657
2658 if (!(flags & SEND_NEWS))
2659 {
2660 if ((mutt_addrlist_count_recips(&e_templ->env->to) == 0) &&
2661 (mutt_addrlist_count_recips(&e_templ->env->cc) == 0) &&
2662 (mutt_addrlist_count_recips(&e_templ->env->bcc) == 0))
2663 {
2664 if (flags & SEND_BATCH)
2665 {
2666 puts(_("No recipients specified"));
2667 goto cleanup;
2668 }
2669
2670 mutt_warning(_("No recipients specified"));
2671 goto main_loop;
2672 }
2673 }
2674
2675 if (mutt_env_to_intl(e_templ->env, &tag, &err))
2676 {
2677 mutt_error(_("Bad IDN in '%s': '%s'"), tag, err);
2678 FREE(&err);
2679 if (flags & SEND_BATCH)
2680 goto cleanup;
2681 goto main_loop;
2682 }
2683
2684 const enum QuadOption c_abort_nosubject = cs_subset_quad(sub, "abort_nosubject");
2685
2686 if (!e_templ->env->subject && !(flags & SEND_BATCH) &&
2687 (query_quadoption(_("No subject, abort sending?"), sub, "abort_nosubject") != MUTT_NO))
2688 {
2689 /* if the abort is automatic, print an error message */
2690 if (c_abort_nosubject == MUTT_YES)
2691 mutt_error(_("No subject specified"));
2692 goto main_loop;
2693 }
2694
2695 if ((flags & SEND_NEWS) && !e_templ->env->subject)
2696 {
2697 mutt_error(_("No subject specified"));
2698 goto main_loop;
2699 }
2700
2701 if ((flags & SEND_NEWS) && !e_templ->env->newsgroups)
2702 {
2703 mutt_error(_("No newsgroup specified"));
2704 goto main_loop;
2705 }
2706
2707 if (!(flags & SEND_BATCH) && abort_for_missing_attachments(e_templ->body, sub))
2708 {
2709 goto main_loop;
2710 }
2711
2712 const bool c_confirm_empty_to = cs_subset_bool(sub, "confirm_empty_to");
2713 if (c_confirm_empty_to && TAILQ_EMPTY(&e_templ->env->to) &&
2714 (query_yesorno(_("No recipients specified in To. Send anyway?"), MUTT_NO) == MUTT_NO))
2715 {
2716 goto main_loop;
2717 }
2718
2719 if (e_templ->body->next)
2720 e_templ->body = mutt_make_multipart(e_templ->body);
2721
2722 /* Ok, we need to do it this way instead of handling all fcc stuff in
2723 * one place in order to avoid going to main_loop with encoded "env"
2724 * in case of error. Ugh. */
2725
2726 mutt_encode_descriptions(e_templ->body, true, sub);
2727
2728 /* Make sure that clear_content and free_clear_content are
2729 * properly initialized -- we may visit this particular place in
2730 * the code multiple times, including after a failed call to
2731 * mutt_protect(). */
2732
2733 clear_content = NULL;
2734 free_clear_content = false;
2735
2736 if (WithCrypto)
2737 {
2738 if (e_templ->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT))
2739 {
2740 /* save the decrypted attachments */
2741 clear_content = e_templ->body;
2742
2743 if ((crypt_get_keys(e_templ, &pgpkeylist, false) == -1) ||
2744 (mutt_protect(e_templ, pgpkeylist, false) == -1))
2745 {
2746 if (mutt_istr_equal(e_templ->body->subtype, "mixed"))
2747 e_templ->body = mutt_remove_multipart(e_templ->body);
2748
2749 FREE(&pgpkeylist);
2750
2751 decode_descriptions(e_templ->body);
2752
2753 if (flags & SEND_BATCH)
2754 {
2755 mutt_message(_("Missing encryption key; mail not sent"));
2756 rc = -1;
2757 goto cleanup;
2758 }
2759
2760 goto main_loop;
2761 }
2762 mutt_encode_descriptions(e_templ->body, false, sub);
2763 }
2764
2765 /* at this point, e_templ->body is one of the following three things:
2766 * - multipart/signed. In this case, clear_content is a child
2767 * - multipart/encrypted. In this case, clear_content exists independently
2768 * - application/pgp. In this case, clear_content exists independently
2769 * - something else. In this case, it's the same as clear_content */
2770
2771 /* This is ugly -- lack of "reporting back" from mutt_protect(). */
2772
2773 if (clear_content && (e_templ->body != clear_content) &&
2774 (e_templ->body->parts != clear_content))
2775 free_clear_content = true;
2776 }
2777
2778 if (OptGui)
2779 mutt_message(_("Sending message..."));
2780
2781 mutt_prepare_envelope(e_templ->env, true, sub);
2782
2783 const bool c_fcc_before_send = cs_subset_bool(sub, "fcc_before_send");
2784 if (c_fcc_before_send)
2785 save_fcc(m, e_templ, fcc, clear_content, pgpkeylist, flags, &finalpath, sub);
2786
2787 i = invoke_mta(m, e_templ, sub);
2788 if (i < 0)
2789 {
2790 if (!(flags & SEND_BATCH))
2791 {
2792 if (!WithCrypto)
2793 ; // do nothing
2794 else if ((e_templ->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)) ||
2795 ((e_templ->security & SEC_SIGN) && (e_templ->body->type == TYPE_APPLICATION)))
2796 {
2797 if (e_templ->body != clear_content)
2798 {
2799 mutt_body_free(&e_templ->body); /* destroy PGP data */
2800 e_templ->body = clear_content; /* restore clear text. */
2801 }
2802 }
2803 else if ((e_templ->security & SEC_SIGN) && (e_templ->body->type == TYPE_MULTIPART))
2804 {
2805 mutt_body_free(&e_templ->body->parts->next); /* destroy sig */
2806 if (mutt_istr_equal(e_templ->body->subtype, "mixed") ||
2807 mutt_istr_equal(e_templ->body->subtype, "signed"))
2808 {
2809 e_templ->body = mutt_remove_multipart(e_templ->body);
2810 }
2811 }
2812
2813 FREE(&pgpkeylist);
2814 mutt_env_free(&e_templ->body->mime_headers); /* protected headers */
2815 mutt_param_delete(&e_templ->body->parameter, "protected-headers");
2816 if (mutt_istr_equal(e_templ->body->subtype, "mixed"))
2817 e_templ->body = mutt_remove_multipart(e_templ->body);
2818 decode_descriptions(e_templ->body);
2819 mutt_unprepare_envelope(e_templ->env);
2820 FREE(&finalpath);
2821 goto main_loop;
2822 }
2823 else
2824 {
2825 puts(_("Could not send the message"));
2826 goto cleanup;
2827 }
2828 }
2829
2830 if (!c_fcc_before_send)
2831 save_fcc(m, e_templ, fcc, clear_content, pgpkeylist, flags, &finalpath, sub);
2832
2833 if (OptGui)
2834 {
2835 mutt_message((i != 0) ? _("Sending in background") :
2836 (flags & SEND_NEWS) ? _("Article posted") :
2837 _("Mail sent"));
2838#ifdef USE_NOTMUCH
2839 const bool c_nm_record = cs_subset_bool(sub, "nm_record");
2840 if (c_nm_record)
2841 nm_record_message(m, finalpath, e_cur);
2842#endif
2843 mutt_sleep(0);
2844 }
2845
2846 if (WithCrypto)
2847 FREE(&pgpkeylist);
2848
2849 if ((WithCrypto != 0) && free_clear_content)
2850 mutt_body_free(&clear_content);
2851
2852 /* set 'replied' flag only if the user didn't change/remove
2853 * In-Reply-To: and References: headers during edit */
2854 if (flags & SEND_REPLY)
2855 {
2856 if (!(flags & SEND_POSTPONED) && m)
2857 {
2858 struct Email **ep = NULL;
2859 ARRAY_FOREACH(ep, ea)
2860 {
2861 struct Email *e = *ep;
2862 mutt_set_flag(m, e, MUTT_REPLIED, is_reply(e, e_templ), true);
2863 }
2864 }
2865 }
2866
2867 rc = 0;
2868
2869cleanup:
2870 buf_pool_release(&fcc);
2871
2872 if (flags & SEND_POSTPONED)
2873 {
2875 {
2876 cs_subset_str_string_set(sub, "pgp_sign_as", pgp_sign_as, NULL);
2877 FREE(&pgp_sign_as);
2878 }
2880 {
2881 cs_subset_str_string_set(sub, "smime_sign_as", smime_sign_as, NULL);
2882 FREE(&smime_sign_as);
2883 }
2884 }
2885
2886 mutt_file_fclose(&fp_tmp);
2887 if (!(flags & SEND_NO_FREE_HEADER))
2888 email_free(&e_templ);
2889
2890 FREE(&finalpath);
2891 return rc;
2892}
2893
2904static bool send_simple_email(struct Mailbox *m, struct EmailArray *ea,
2905 const char *mailto, const char *subj, const char *body)
2906{
2907 struct Email *e = email_new();
2908
2909 /* envelope */
2910 e->env = mutt_env_new();
2911 mutt_parse_mailto(e->env, NULL, mailto);
2912 if (!e->env->subject)
2913 {
2914 mutt_env_set_subject(e->env, subj);
2915 }
2916 if (TAILQ_EMPTY(&e->env->to) && !mutt_addrlist_parse(&e->env->to, NULL))
2917 {
2918 mutt_warning(_("No recipient specified"));
2919 }
2920
2921 /* body */
2922 e->body = mutt_body_new();
2923 char ctype[] = "text/plain";
2924 mutt_parse_content_type(ctype, e->body);
2925
2926 struct Buffer *tempfile = buf_pool_get();
2927 buf_mktemp(tempfile);
2928 if (body)
2929 {
2930 FILE *fp = mutt_file_fopen(buf_string(tempfile), "w+");
2931 if (!fp)
2932 {
2933 email_free(&e);
2934 buf_pool_release(&tempfile);
2935 return false;
2936 }
2937 fprintf(fp, "%s\n", body);
2938 mutt_file_fclose(&fp);
2939 }
2940 e->body->filename = buf_strdup(tempfile);
2941 e->body->unlink = true;
2942 buf_pool_release(&tempfile);
2943
2944 const int rc = mutt_send_message(SEND_DRAFT_FILE, e, NULL, m, ea, NeoMutt->sub);
2945 return rc >= 0;
2946}
2947
2955bool mutt_send_list_subscribe(struct Mailbox *m, struct Email *e)
2956{
2957 if (!e || !e->env)
2958 {
2959 return false;
2960 }
2961
2962 const char *mailto = e->env->list_subscribe;
2963 if (!mailto)
2964 {
2965 mutt_warning(_("No List-Subscribe header found"));
2966 return false;
2967 }
2968
2969 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
2970 ARRAY_ADD(&ea, e);
2971 bool rc = send_simple_email(m, &ea, mailto, "Subscribe", "subscribe");
2972 ARRAY_FREE(&ea);
2973
2974 return rc;
2975}
2976
2984bool mutt_send_list_unsubscribe(struct Mailbox *m, struct Email *e)
2985{
2986 if (!e || !e->env)
2987 {
2988 return false;
2989 }
2990
2991 const char *mailto = e->env->list_unsubscribe;
2992 if (!mailto)
2993 {
2994 mutt_warning(_("No List-Unsubscribe header found"));
2995 return false;
2996 }
2997
2998 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
2999 ARRAY_ADD(&ea, e);
3000 bool rc = send_simple_email(m, &ea, mailto, "Unsubscribe", "unsubscribe");
3001 ARRAY_FREE(&ea);
3002
3003 return rc;
3004}
struct Address * mutt_addr_create(const char *personal, const char *mailbox)
Create and populate a new Address.
Definition address.c:414
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition address.c:769
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition address.c:1464
void mutt_addr_free(struct Address **ptr)
Free a single Address.
Definition address.c:462
bool mutt_addr_valid_msgid(const char *msgid)
Is this a valid Message ID?
Definition address.c:796
bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
Compare two e-mail addresses.
Definition address.c:896
void mutt_addrlist_append(struct AddressList *al, struct Address *a)
Append an Address to an AddressList.
Definition address.c:1484
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition address.c:1382
size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
Write an Address to a buffer.
Definition address.c:1210
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition address.c:644
struct Address * mutt_addr_copy(const struct Address *addr)
Copy the real address.
Definition address.c:749
void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
Remove cross-references.
Definition address.c:1437
int mutt_addrlist_count_recips(const struct AddressList *al)
Count the number of Addresses with valid recipients.
Definition address.c:876
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition address.c:480
void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
Prepend an Address to an AddressList.
Definition address.c:1495
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition address.c:1297
bool mutt_addrlist_search(const struct AddressList *haystack, const struct Address *needle)
Search for an e-mail address in a list.
Definition address.c:913
void mutt_addrlist_dedupe(struct AddressList *al)
Remove duplicate addresses.
Definition address.c:1401
const struct Address * cs_subset_address(const struct ConfigSubset *sub, const char *name)
Get an Address config item by name.
Email Address Handling.
const struct CompleteOps CompleteAliasOps
Auto-Completion of Aliases.
Definition complete.c:108
Email Aliases.
void mutt_expand_aliases(struct AddressList *al)
Expand aliases in a List of Addresses.
Definition alias.c:296
bool mutt_addr_is_user(const struct Address *addr)
Does the address belong to the user.
Definition alias.c:600
void mutt_expand_aliases_env(struct Envelope *env)
Expand aliases in all the fields of an Envelope.
Definition alias.c:310
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:157
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition array.h:74
#define ARRAY_SIZE(head)
The number of elements stored.
Definition array.h:87
#define ARRAY_FREE(head)
Release all memory.
Definition array.h:209
#define ARRAY_GET(head, idx)
Return the element at index.
Definition array.h:109
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition array.h:58
void mutt_actx_free(struct AttachCtx **ptr)
Free an Attachment Context.
Definition attach.c:198
void mutt_parse_mime_message(struct Email *e, FILE *fp)
Parse a MIME email.
Definition commands.c:627
GUI display the mailboxes in a side panel.
char * AutocryptDefaultKey
Autocrypt default key id (used for postponing messages)
Definition config.c:38
Autocrypt end-to-end encryption.
int mutt_autocrypt_set_sign_as_default_key(struct Email *e)
Set the Autocrypt default key for signing.
Definition autocrypt.c:697
Select a Mailbox from a list.
#define MUTT_SEL_NO_FLAGS
No flags are set.
Definition lib.h:58
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
struct Buffer * buf_new(const char *str)
Allocate a new Buffer.
Definition buffer.c:304
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition buffer.c:337
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
@ CMD_SEND_HOOK
:send-hook
Definition command.h:107
@ CMD_MESSAGE_HOOK
:message-hook
Definition command.h:94
@ CMD_SEND2_HOOK
:send2-hook
Definition command.h:106
@ CMD_REPLY_HOOK
:reply-hook
Definition command.h:102
GUI editor for an email's headers.
#define MUTT_COMPOSE_NOFREEHEADER
Don't free the header when closing compose dialog.
Definition lib.h:54
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition helpers.c:217
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition helpers.c:242
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition helpers.c:192
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition helpers.c:168
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
const struct Expando * cs_subset_expando(const struct ConfigSubset *sub, const char *name)
Get an Expando config item by name.
Convenience wrapper for the config headers.
int cs_str_initial_get(const struct ConfigSet *cs, const char *name, struct Buffer *result)
Get the initial, or parent, value of a config item.
Definition set.c:594
int mutt_body_copy(FILE *fp, struct Body **b_dst, struct Body *b_src)
Create a send-mode duplicate from a receive-mode body.
Definition copy_body.c:50
int mutt_copy_message(FILE *fp_out, struct Email *e, struct Message *msg, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
Copy a message from a Mailbox.
Definition copy_email.c:917
#define CH_DECODE
Do RFC2047 header decoding.
Definition copy_email.h:58
#define MUTT_CM_WEED
Weed message/rfc822 attachment headers.
Definition copy_email.h:43
#define MUTT_CM_REPLYING
Replying the message.
Definition copy_email.h:46
#define MUTT_CM_PREFIX
Quote the header and body.
Definition copy_email.h:39
#define MUTT_CM_DECODE
Decode the message body into text/plain.
Definition copy_email.h:40
#define CH_WEED
Weed the headers?
Definition copy_email.h:57
#define CH_REORDER
Re-order output of headers (specified by 'header-order')
Definition copy_email.h:63
#define MUTT_CM_CHARCONV
Perform character set conversions.
Definition copy_email.h:44
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition copy_email.h:54
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition copy_email.h:37
#define MUTT_CM_NOHEADER
Don't copy the message header.
Definition copy_email.h:38
uint16_t CopyMessageFlags
Flags for mutt_copy_message(), e.g. MUTT_CM_NOHEADER.
Definition copy_email.h:36
Convenience wrapper for the core headers.
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition mailbox.h:48
@ MUTT_IMAP
'IMAP' Mailbox type
Definition mailbox.h:49
void crypt_opportunistic_encrypt(struct Email *e)
Can all recipients be determined.
Definition crypt.c:1045
bool crypt_valid_passphrase(SecurityFlags flags)
Check that we have a usable passphrase, ask if not.
Definition crypt.c:131
bool mutt_should_hide_protected_subject(struct Email *e)
Should NeoMutt hide the protected subject?
Definition crypt.c:1100
int mutt_protect(struct Email *e, char *keylist, bool postpone)
Encrypt and/or sign a message.
Definition crypt.c:156
int crypt_get_keys(struct Email *e, char **keylist, bool oppenc_mode)
Check we have all the keys we need.
Definition crypt.c:961
bool crypt_has_module_backend(SecurityFlags type)
Is there a crypto backend for a given type?
Definition cryptglue.c:170
struct Body * crypt_pgp_make_key_attachment(void)
Wrapper for CryptModuleSpecs::pgp_make_key_attachment()
Definition cryptglue.c:304
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition curs_lib.c:116
bool mutt_is_quote_line(char *line, regmatch_t *pmatch)
Is a line of message text a quote?
Definition display.c:323
int mutt_make_string(struct Buffer *buf, size_t max_cols, const struct Expando *exp, struct Mailbox *m, int inpgr, struct Email *e, MuttFormatFlags flags, const char *progress)
Create formatted strings using mailbox expandos.
Definition dlg_index.c:802
Edit a string.
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition wdata.h:42
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition body.c:58
struct Body * mutt_body_new(void)
Create a new Body.
Definition body.c:44
struct Email * email_new(void)
Create a new Email.
Definition email.c:77
void email_free(struct Email **ptr)
Free an Email.
Definition email.c:46
void mutt_edit_headers(const char *editor, const char *body, struct Email *e, struct Buffer *fcc)
Let the user edit the message header and body.
Definition header.c:181
Structs that make up an email.
void mutt_parse_content_type(const char *s, struct Body *b)
Parse a content type.
Definition parse.c:468
bool mutt_parse_mailto(struct Envelope *env, char **body, const char *src)
Parse a mailto:// url.
Definition parse.c:1762
char * mutt_extract_message_id(const char *s, size_t *len)
Find a message-id.
Definition parse.c:406
const char * mutt_get_name(const struct Address *a)
Pick the best name to display from an address.
Definition sort.c:138
int mutt_env_to_intl(struct Envelope *env, const char **tag, char **err)
Convert an Envelope's Address fields to Punycode format.
Definition envelope.c:350
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition envelope.c:125
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition envelope.c:45
void mutt_env_set_subject(struct Envelope *env, const char *subj)
Set both subject and real_subj to subj.
Definition envelope.c:68
void mutt_env_to_local(struct Envelope *env)
Convert an Envelope's Address fields to local format.
Definition envelope.c:316
int expando_filter(const struct Expando *exp, const struct ExpandoRenderCallback *erc, void *data, MuttFormatFlags flags, int max_cols, char **env_list, struct Buffer *buf)
Render an Expando and run the result through a filter.
Definition filter.c:139
Parse Expando string.
const struct ExpandoRenderCallback GreetingRenderCallbacks[]
Callbacks for Greeting Expandos.
Greeting Expando definitions.
char * msgid_generate(void)
Generate a Message-Id.
Message Id Expando definitions.
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition file.c:222
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition file.c:1427
time_t mutt_file_decrease_mtime(const char *fp, struct stat *st)
Decrease a file's modification time by 1 second.
Definition file.c:902
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition file.c:652
#define mutt_file_fclose(FP)
Definition file.h:139
#define mutt_file_fopen(PATH, MODE)
Definition file.h:138
void mutt_set_flag(struct Mailbox *m, struct Email *e, enum MessageType flag, bool bf, bool upd_mbox)
Set a flag on an email.
Definition flags.c:54
bool OptGui
(pseudo) when the gui (and curses) are started
Definition globals.c:48
bool OptNewsSend
(pseudo) used to change behavior when posting
Definition globals.c:54
Global variables.
bool mutt_is_mail_list(const struct Address *addr)
Is this the email address of a mailing list?
Definition maillist.c:45
int dlg_compose(struct Email *e, struct Buffer *fcc, uint8_t flags, struct ConfigSubset *sub)
Allow the user to edit the message envelope -.
int mw_enter_fname(const char *prompt, struct Buffer *fname, bool mailbox, struct Mailbox *m, bool multiple, char ***files, int *numfiles, SelectFileFlags flags)
Ask the user to select a file -.
Definition curs_lib.c:236
int mw_get_field(const char *prompt, struct Buffer *buf, CompletionFlags complete, enum HistoryClass hclass, const struct CompleteOps *comp_api, void *cdata)
Ask the user for a string -.
Definition window.c:270
int mw_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question -.
Definition question.c:62
#define mutt_warning(...)
Definition logging2.h:92
#define mutt_error(...)
Definition logging2.h:94
#define mutt_message(...)
Definition logging2.h:93
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
#define mutt_perror(...)
Definition logging2.h:95
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox?
Definition imap.c:2543
Convenience wrapper for the gui headers.
bool mutt_prefer_as_attachment(struct Body *b)
Do we want this part as an attachment?
Definition handler.c:1877
Read/write command history from/to a file.
@ HC_ALIAS
Aliases.
Definition lib.h:56
@ HC_OTHER
Miscellaneous strings.
Definition lib.h:60
void mutt_select_fcc(struct Buffer *path, struct Email *e)
Select the FCC path for an email.
Definition exec.c:252
void exec_message_hook(struct Mailbox *m, struct Email *e, enum CommandId id)
Perform a message hook.
Definition exec.c:135
Hook Commands.
IMAP network mailbox.
GUI manage the main index (list of emails)
void mutt_list_copy_tail(struct ListHead *dst, const struct ListHead *src)
Copy a list into another list.
Definition list.c:275
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition list.c:65
struct ListNode * mutt_list_find(const struct ListHead *h, const char *data)
Find a string in a List.
Definition list.c:103
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition list.c:46
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition list.c:123
@ LL_DEBUG5
Log at debug level 5.
Definition logging2.h:49
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
#define countof(x)
Definition memory.h:49
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:53
@ ENC_8BIT
8-bit text
Definition mime.h:50
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition mime.h:37
@ TYPE_APPLICATION
Type: 'application/*'.
Definition mime.h:33
@ TYPE_TEXT
Type: 'text/*'.
Definition mime.h:38
@ DISP_INLINE
Content is inline.
Definition mime.h:62
@ MODULE_ID_SEND
ModuleSend, Send
Definition module_api.h:90
struct Body * mutt_remove_multipart(struct Body *b)
Extract the multipart body if it exists.
Definition multipart.c:126
struct Body * mutt_make_multipart(struct Body *b)
Create a multipart email.
Definition multipart.c:100
Manipulate multipart Emails.
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition date.c:457
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition filter.c:220
Convenience wrapper for the library headers.
#define FALLTHROUGH
Definition lib.h:117
#define _(a)
Definition message.h:28
bool mutt_path_canon(struct Buffer *path, const char *homedir, bool is_dir)
Create the canonical version of a path.
Definition path.c:248
bool mutt_regex_match(const struct Regex *regex, const char *str)
Shorthand to mutt_regex_capture()
Definition regex.c:614
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:674
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition string.c:805
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition string.c:610
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:662
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:246
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
Many unsorted constants and some structs.
@ MUTT_REPLIED
Messages that have been replied to.
Definition mutt.h:91
bool mutt_edit_attachment(struct Body *b)
Edit an attachment.
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
NeoMutt Logging.
void mutt_sleep(short s)
Sleep for a while.
Definition muttlib.c:786
void pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition muttlib.c:427
bool mutt_needs_mailcap(struct Body *b)
Does this type need a mailcap entry do display.
Definition muttlib.c:367
void expand_path(struct Buffer *buf, bool regex)
Create the canonical path.
Definition muttlib.c:121
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition muttlib.c:644
Some miscellaneous functions.
int mx_msg_close(struct Mailbox *m, struct Message **ptr)
Close a message.
Definition mx.c:1182
struct Message * mx_msg_open(struct Mailbox *m, struct Email *e)
Return a stream pointer for a message.
Definition mx.c:1136
API for mailboxes.
API for encryption/signing of emails.
#define SEC_INLINE
Email has an inline signature.
Definition lib.h:93
#define SEC_AUTOCRYPT
(Autocrypt) Message will be, or was Autocrypt encrypt+signed
Definition lib.h:95
#define SEC_OPPENCRYPT
Opportunistic encrypt mode.
Definition lib.h:94
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition lib.h:98
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition lib.h:99
#define SEC_NO_FLAGS
No flags are set.
Definition lib.h:85
#define SEC_ENCRYPT
Email is encrypted.
Definition lib.h:86
#define WithCrypto
Definition lib.h:124
#define SEC_AUTOCRYPT_OVERRIDE
(Autocrypt) Indicates manual set/unset of encryption
Definition lib.h:96
#define SEC_SIGN
Email is signed.
Definition lib.h:87
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:585
Nntp-specific Mailbox data.
Notmuch virtual mailbox type.
int nm_record_message(struct Mailbox *m, char *path, struct Email *e)
Add a message to the Notmuch database.
Definition notmuch.c:1941
GUI display a file/email/help in a viewport with paging.
void mutt_param_delete(struct ParameterList *pl, const char *attribute)
Delete a matching Parameter.
Definition parameter.c:143
void mutt_param_set(struct ParameterList *pl, const char *attribute, const char *value)
Set a Parameter.
Definition parameter.c:111
bool mutt_is_subscribed_list_recipient(bool all_addr, struct Envelope *env)
Matches subscribed mailing lists.
Definition exec.c:494
bool mutt_is_list_recipient(bool all_addr, struct Envelope *env)
Matches known mailing lists.
Definition exec.c:507
Match patterns to emails.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
Postponed Emails.
int mutt_num_postponed(struct Mailbox *m, bool force)
Return the number of postponed messages.
Definition postpone.c:67
void mutt_update_num_postponed(void)
Force the update of the number of postponed messages.
Definition postpone.c:174
int mutt_get_postponed(struct Mailbox *m_cur, struct Email *hdr, struct Email **cur, struct Buffer *fcc)
Recall a postponed message.
Definition postpone.c:667
int mutt_prepare_template(FILE *fp, struct Mailbox *m, struct Email *e_new, struct Email *e, bool resend)
Prepare a message template.
Definition postpone.c:492
QuadOption
Possible values for a quad-option.
Definition quad.h:36
@ MUTT_ABORT
User aborted the question (with Ctrl-G)
Definition quad.h:37
@ MUTT_ASKNO
Ask the user, defaulting to 'No'.
Definition quad.h:40
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition quad.h:38
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition quad.h:39
Ask the user a question.
enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition question.c:378
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition question.c:326
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition queue.h:792
#define STAILQ_FIRST(head)
Definition queue.h:388
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
#define TAILQ_FIRST(head)
Definition queue.h:780
#define STAILQ_EMPTY(head)
Definition queue.h:382
#define TAILQ_SWAP(head1, head2, type, field)
Definition queue.h:937
#define TAILQ_FOREACH_REVERSE(var, head, headname, field)
Definition queue.h:802
#define TAILQ_REMOVE(head, elm, field)
Definition queue.h:901
#define TAILQ_NEXT(elm, field)
Definition queue.h:889
#define TAILQ_EMPTY(head)
Definition queue.h:778
#define STAILQ_NEXT(elm, field)
Definition queue.h:439
void mutt_generate_recvattach_list(struct AttachCtx *actx, struct Email *e, struct Body *b, FILE *fp, int parent_type, int level, bool decrypted)
Create a list of attachments.
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition render.h:33
void rfc2047_encode(char **pd, const char *specials, int col, const struct Slist *charsets)
RFC-2047-encode a string.
Definition rfc2047.c:632
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition rfc2047.c:665
void mutt_rfc3676_space_stuff(struct Email *e)
Perform RFC3676 space stuffing on an Email.
Definition rfc3676.c:490
int mutt_write_mime_body(struct Body *b, FILE *fp, struct ConfigSubset *sub)
Write a MIME part.
Definition body.c:300
Convenience wrapper for the send headers.
int mutt_rfc822_write_header(FILE *fp, struct Envelope *env, struct Body *b, enum MuttWriteHeaderMode mode, bool privacy, bool hide_protected_subject, struct ConfigSubset *sub)
Write out one RFC822 header line.
Definition header.c:578
Convenience wrapper for the send headers.
@ MUTT_WRITE_HEADER_NORMAL
A normal Email, write full header + MIME headers.
Definition header.h:38
Send private Module data.
void mutt_make_misc_reply_headers(struct Envelope *env, struct Envelope *env_cur, struct ConfigSubset *sub)
Set subject for a reply.
Definition send.c:1007
static int postpone_message(struct Email *e_post, struct Email *e_cur, const char *fcc, SendFlags flags, struct ConfigSubset *sub)
Save an Email for another day.
Definition send.c:1860
static int include_reply(struct Mailbox *m, struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Generate the reply text for an email.
Definition send.c:713
static bool is_reply(struct Email *reply, struct Email *orig)
Is one email a reply to another?
Definition send.c:1601
void mutt_encode_descriptions(struct Body *b, bool recurse, struct ConfigSubset *sub)
RFC2047 encode the content-descriptions.
Definition send.c:1492
void mutt_make_attribution_intro(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add "on DATE, PERSON wrote" header.
Definition send.c:664
static int save_fcc(struct Mailbox *m, struct Email *e, struct Buffer *fcc, struct Body *clear_content, char *pgpkeylist, SendFlags flags, char **finalpath, struct ConfigSubset *sub)
Save an Email to a 'sent mail' folder.
Definition send.c:1665
static int envelope_defaults(struct Envelope *env, struct EmailArray *ea, SendFlags flags, struct ConfigSubset *sub)
Fill in some defaults for a new email.
Definition send.c:1085
int mutt_edit_address(struct AddressList *al, const char *field, bool expand_aliases)
Edit an email address.
Definition send.c:183
void mutt_forward_intro(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add the "start of forwarded message" text.
Definition send.c:456
void mutt_make_forward_subject(struct Envelope *env, struct Email *e, struct ConfigSubset *sub)
Create a subject for a forwarded email.
Definition send.c:987
static void make_reference_headers(struct EmailArray *ea, struct Envelope *env, struct ConfigSubset *sub)
Generate reference headers for an email.
Definition send.c:1053
static const struct AddressList * choose_default_to(const struct Address *from, const struct Envelope *env, struct ConfigSubset *sub)
Pick the best 'to:' value.
Definition send.c:762
void mutt_fix_reply_recipients(struct Envelope *env, struct ConfigSubset *sub)
Remove duplicate recipients.
Definition send.c:957
static char * nntp_get_header(const char *s)
Get the trimmed header.
Definition send.c:364
int mutt_resend_message(FILE *fp, struct Mailbox *m, struct Email *e_cur, struct ConfigSubset *sub)
Resend an email.
Definition send.c:1552
static int include_forward(struct Mailbox *m, struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Write out a forwarded message.
Definition send.c:508
static int generate_body(FILE *fp_tmp, struct Email *e, SendFlags flags, struct Mailbox *m, struct EmailArray *ea, struct ConfigSubset *sub)
Create a new email body.
Definition send.c:1158
static void remove_user(struct AddressList *al, bool leave_only)
Remove any address which matches the current user.
Definition send.c:137
static void add_message_id(struct ListHead *head, struct Envelope *env)
Add the email's message ID to a list.
Definition send.c:944
static void add_mailing_lists(struct AddressList *out, const struct AddressList *t, const struct AddressList *c)
Search Address lists for mailing lists.
Definition send.c:156
int mutt_fetch_recips(struct Envelope *out, struct Envelope *in, SendFlags flags, struct ConfigSubset *sub)
Generate recpients for a reply email.
Definition send.c:874
static void mutt_make_greeting(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add greetings string.
Definition send.c:688
static int invoke_mta(struct Mailbox *m, struct Email *e, struct ConfigSubset *sub)
Send an email.
Definition send.c:1429
struct Address * mutt_default_from(struct ConfigSubset *sub)
Get a default 'from' Address.
Definition send.c:1396
static void process_user_recips(struct Envelope *env)
Process the user headers.
Definition send.c:374
bool mutt_send_list_unsubscribe(struct Mailbox *m, struct Email *e)
Send a mailing-list unsubscription email.
Definition send.c:2984
static void format_attribution(const struct Expando *exp, struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Format an attribution prefix/suffix.
Definition send.c:641
static void process_user_header(struct Envelope *env)
Process the user headers.
Definition send.c:402
static int edit_envelope(struct Envelope *en, SendFlags flags, struct ConfigSubset *sub)
Edit Envelope fields.
Definition send.c:229
static bool send_simple_email(struct Mailbox *m, struct EmailArray *ea, const char *mailto, const char *subj, const char *body)
Compose an email given a few basic ingredients.
Definition send.c:2904
static bool abort_for_missing_attachments(const struct Body *b, struct ConfigSubset *sub)
Should we abort sending because of missing attachments?
Definition send.c:1985
static int default_to(struct AddressList *to, struct Envelope *env, SendFlags flags, int hmfupto, struct ConfigSubset *sub)
Generate default email addresses.
Definition send.c:788
static void set_reverse_name(struct AddressList *al, struct Envelope *env, struct ConfigSubset *sub)
Try to set the 'from' field from the recipients.
Definition send.c:1343
static void fix_end_of_file(const char *data)
Ensure a file ends with a linefeed.
Definition send.c:1527
static bool search_attach_keyword(char *filename, struct ConfigSubset *sub)
Search an email for 'attachment' keywords.
Definition send.c:1620
void mutt_forward_trailer(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add a "end of forwarded message" text.
Definition send.c:480
int mutt_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile, struct Mailbox *m, struct EmailArray *ea, struct ConfigSubset *sub)
Send an email.
Definition send.c:2037
static bool is_text_plain(const struct Body *b)
Is a Body a text/plain MIME part?
Definition send.c:1974
static int inline_forward_attachments(struct Mailbox *m, struct Email *e, struct Body ***plast, enum QuadOption *forwardq, struct ConfigSubset *sub)
Add attachments to an email, inline.
Definition send.c:567
bool mutt_send_list_subscribe(struct Mailbox *m, struct Email *e)
Send a mailing-list subscription email.
Definition send.c:2955
static void add_references(struct ListHead *head, struct Envelope *env)
Add the email's references to a list.
Definition send.c:933
static void decode_descriptions(struct Body *b)
RFC2047 decode them in case of an error.
Definition send.c:1510
void mutt_make_attribution_trailer(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add suffix to replied email text.
Definition send.c:675
void mutt_set_followup_to(struct Envelope *env, struct ConfigSubset *sub)
Set followup-to field.
Definition send.c:1272
static void append_signature(FILE *fp, struct ConfigSubset *sub)
Append a signature to an email.
Definition send.c:100
void mutt_add_to_reference_headers(struct Envelope *env, struct Envelope *env_cur, struct ConfigSubset *sub)
Generate references for a reply email.
Definition send.c:1035
Prepare and send an email.
#define SEND_POSTPONED_FCC
Used by mutt_get_postponed() to signal that the Mutt-Fcc header field was present.
Definition send.h:50
#define SEND_BATCH
Send email in batch mode (without user interaction)
Definition send.h:47
#define SEND_GROUP_CHAT_REPLY
Reply to all recipients preserving To/Cc.
Definition send.h:54
#define SEND_NO_FREE_HEADER
Used by the -E flag.
Definition send.h:51
#define SEND_DRAFT_FILE
Used by the -H flag.
Definition send.h:52
uint32_t SendFlags
Flags for mutt_send_message(), e.g. SEND_REPLY.
Definition send.h:40
#define SEND_GROUP_REPLY
Reply to all.
Definition send.h:43
#define SEND_LIST_REPLY
Reply to mailing list.
Definition send.h:44
#define SEND_KEY
Mail a PGP public key.
Definition send.h:48
#define SEND_POSTPONED
Recall a postponed email.
Definition send.h:46
#define SEND_CONSUMED_STDIN
stdin has been read; don't read it twice
Definition send.h:57
#define SEND_TO_SENDER
Compose new email to sender.
Definition send.h:53
#define SEND_CLI_CRYPTO
Enable message security in modes that by default don't enable it.
Definition send.h:58
#define SEND_RESEND
Reply using the current email as a template.
Definition send.h:49
#define SEND_REPLY
Reply to sender.
Definition send.h:42
#define SEND_REVIEW_TO
Allow the user to edit the To field.
Definition send.h:56
#define SEND_NEWS
Reply to a news article.
Definition send.h:55
#define SEND_FORWARD
Forward email.
Definition send.h:45
void mutt_update_encoding(struct Body *b, struct ConfigSubset *sub)
Update the encoding type.
Definition sendlib.c:421
const char * mutt_fqdn(bool may_hide_host, const struct ConfigSubset *sub)
Get the Fully-Qualified Domain Name.
Definition sendlib.c:706
int mutt_write_multiple_fcc(const char *path, struct Email *e, const char *msgid, bool post, char *fcc, char **finalpath, struct ConfigSubset *sub)
Handle FCC with multiple, comma separated entries.
Definition sendlib.c:964
void mutt_unprepare_envelope(struct Envelope *env)
Undo the encodings of mutt_prepare_envelope()
Definition sendlib.c:778
void mutt_prepare_envelope(struct Envelope *env, bool final, struct ConfigSubset *sub)
Prepare an email header.
Definition sendlib.c:739
struct Body * mutt_make_message_attach(struct Mailbox *m, struct Email *e, bool attach_msg, struct ConfigSubset *sub)
Create a message attachment.
Definition sendlib.c:453
int mutt_write_fcc(const char *path, struct Email *e, const char *msgid, bool post, const char *fcc, char **finalpath, struct ConfigSubset *sub)
Write email to FCC mailbox.
Definition sendlib.c:1017
Miscellaneous functions for sending an email.
int mutt_invoke_sendmail(struct Mailbox *m, struct AddressList *from, struct AddressList *to, struct AddressList *cc, struct AddressList *bcc, const char *msg, bool eightbit, struct ConfigSubset *sub)
Run sendmail.
Definition sendmail.c:298
Send email using sendmail.
#define ASSERT(COND)
Definition signal2.h:59
int mutt_smtp_send(const struct AddressList *from, const struct AddressList *to, const struct AddressList *cc, const struct AddressList *bcc, const char *msgfile, bool eightbit, struct ConfigSubset *sub)
Send a message using SMTP.
Definition smtp.c:1100
Send email to an SMTP server.
#define NONULL(x)
Definition string2.h:44
#define SKIPWS(ch)
Definition string2.h:52
An email address.
Definition address.h:35
struct Buffer * personal
Real name of address.
Definition address.h:36
bool group
Group mailbox?
Definition address.h:38
struct Buffer * mailbox
Mailbox and host address.
Definition address.h:37
A set of attachments.
Definition attach.h:63
FILE * fp_root
Used by recvattach for updating.
Definition attach.h:65
struct Email * email
Used by recvattach for updating.
Definition attach.h:64
struct AttachPtr ** idx
Array of attachments.
Definition attach.h:67
short idxlen
Number of attachmentes.
Definition attach.h:68
struct Body * body
Attachment.
Definition attach.h:36
FILE * fp
Used in the recvattach menu.
Definition attach.h:37
The body of an email.
Definition body.h:36
struct Body * parts
parts of a multipart or message/rfc822
Definition body.h:73
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition body.h:68
struct Envelope * mime_headers
Memory hole protected headers.
Definition body.h:76
struct ParameterList parameter
Parameters of the content-type.
Definition body.h:63
bool use_disp
Content-Disposition uses filename= ?
Definition body.h:47
unsigned int disposition
content-disposition, ContentDisposition
Definition body.h:42
struct Body * next
next attachment in the list
Definition body.h:72
char * subtype
content-type subtype
Definition body.h:61
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition body.h:41
unsigned int type
content-type primary type, ContentType
Definition body.h:40
char * filename
When sending a message, this is the file to which this structure refers.
Definition body.h:59
String manipulation buffer.
Definition buffer.h:36
size_t dsize
Length of data.
Definition buffer.h:39
A set of inherited config items.
Definition subset.h:46
struct ConfigSet * cs
Parent ConfigSet.
Definition subset.h:50
The envelope/body of an email.
Definition email.h:39
bool read
Email is read.
Definition email.h:50
struct Envelope * env
Envelope information.
Definition email.h:68
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib....
Definition email.h:43
struct Body * body
List of MIME parts.
Definition email.h:69
bool old
Email is seen, but unread.
Definition email.h:49
bool replied
Email has been replied to.
Definition email.h:51
time_t received
Time when the message was placed in the mailbox.
Definition email.h:61
The header of an Email.
Definition envelope.h:57
struct ListHead userhdrs
user defined headers
Definition envelope.h:85
char * list_subscribe
This stores a mailto URL, or nothing.
Definition envelope.h:68
char *const subject
Email's subject.
Definition envelope.h:70
struct AddressList to
Email's 'To' list.
Definition envelope.h:60
char * followup_to
List of 'followup-to' fields.
Definition envelope.h:80
struct AddressList reply_to
Email's 'reply-to'.
Definition envelope.h:64
char * message_id
Message ID.
Definition envelope.h:73
char * x_comment_to
List of 'X-comment-to' fields.
Definition envelope.h:81
struct AddressList x_original_to
Email's 'X-Original-to'.
Definition envelope.h:66
char * newsgroups
List of newsgroups.
Definition envelope.h:78
struct AddressList mail_followup_to
Email's 'mail-followup-to'.
Definition envelope.h:65
struct AddressList cc
Email's 'Cc' list.
Definition envelope.h:61
struct ListHead references
message references (in reverse order)
Definition envelope.h:83
struct ListHead in_reply_to
in-reply-to header content
Definition envelope.h:84
struct AddressList bcc
Email's 'Bcc' list.
Definition envelope.h:62
char * list_post
This stores a mailto URL, or nothing.
Definition envelope.h:67
char *const real_subj
Offset of the real subject.
Definition envelope.h:71
char * list_unsubscribe
This stores a mailto URL, or nothing.
Definition envelope.h:69
struct AddressList from
Email's 'From' list.
Definition envelope.h:59
Parsed Expando trees.
Definition expando.h:41
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
A mailbox.
Definition mailbox.h:78
enum MailboxType type
Mailbox type.
Definition mailbox.h:101
void * mdata
Driver specific data.
Definition mailbox.h:131
A local copy of an email.
Definition message.h:34
FILE * fp
pointer to the message data
Definition message.h:35
Container for Accounts, Notifications.
Definition neomutt.h:41
char ** env
Private copy of the environment variables.
Definition neomutt.h:58
char * username
User's login name.
Definition neomutt.h:57
char * home_dir
User's home directory.
Definition neomutt.h:56
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
NNTP-specific Mailbox data -.
Definition mdata.h:34
Cached regular expression.
Definition regex3.h:85
regex_t * regex
compiled expression
Definition regex3.h:87
Send private Module data.
Definition module_data.h:32
struct ListHead user_header
Custom headers to add to outgoing emails.
Definition module_data.h:33
String list.
Definition slist.h:37
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition subset.c:303
int cs_subset_str_string_set(const struct ConfigSubset *sub, const char *name, const char *value, struct Buffer *err)
Set a config item by string.
Definition subset.c:392
#define buf_mktemp(buf)
Definition tmp.h:33