NeoMutt  2025-12-11-769-g906513
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"
94#endif
95
101static void append_signature(FILE *fp, struct ConfigSubset *sub)
102{
103 const char *const c_signature = cs_subset_path(sub, "signature");
104 if (!c_signature)
105 return;
106
107 // If the user hasn't set $signature, don't warn them if it doesn't exist
108 struct Buffer *def_sig = buf_pool_get();
109 cs_str_initial_get(sub->cs, "signature", def_sig);
110 mutt_path_canon(def_sig, NeoMutt->home_dir, false);
111 bool notify_missing = !mutt_str_equal(c_signature, buf_string(def_sig));
112 buf_pool_release(&def_sig);
113
114 pid_t pid = 0;
115 FILE *fp_tmp = mutt_open_read(c_signature, &pid);
116 if (!fp_tmp)
117 {
118 if (notify_missing)
119 mutt_perror("%s", c_signature);
120 return;
121 }
122
123 const bool c_sig_dashes = cs_subset_bool(sub, "sig_dashes");
124 if (c_sig_dashes)
125 fputs("\n-- \n", fp);
126 mutt_file_copy_stream(fp_tmp, fp);
127 mutt_file_fclose(&fp_tmp);
128 if (pid != -1)
129 filter_wait(pid);
130}
131
138static void remove_user(struct AddressList *al, bool leave_only)
139{
140 struct Address *a = NULL, *tmp = NULL;
141 TAILQ_FOREACH_SAFE(a, al, entries, tmp)
142 {
143 if (mutt_addr_is_user(a) && (!leave_only || TAILQ_NEXT(a, entries)))
144 {
145 TAILQ_REMOVE(al, a, entries);
146 mutt_addr_free(&a);
147 }
148 }
149}
150
157static void add_mailing_lists(struct AddressList *out, const struct AddressList *t,
158 const struct AddressList *c)
159{
160 const struct AddressList *const als[] = { t, c };
161
162 for (size_t i = 0; i < countof(als); i++)
163 {
164 const struct AddressList *al = als[i];
165 struct Address *a = NULL;
166 TAILQ_FOREACH(a, al, entries)
167 {
168 if (!a->group && mutt_is_mail_list(a))
169 {
171 }
172 }
173 }
174}
175
184int mutt_edit_address(struct AddressList *al, const char *field, bool expand_aliases)
185{
186 int rc = 0;
187 struct Buffer *buf = buf_pool_get();
188 buf_alloc(buf, 8192);
189 char *err = NULL;
190 int idna_ok = 0;
191
192 do
193 {
195 buf_reset(buf);
196 mutt_addrlist_write(al, buf, false);
197 if (!buf_is_empty(buf))
198 buf_addstr(buf, ", ");
199
200 if (mw_get_field(field, buf, MUTT_COMP_NO_FLAGS, HC_ALIAS, &CompleteAliasOps, NULL) != 0)
201 {
202 rc = -1;
203 goto done;
204 }
207 if (expand_aliases)
209 idna_ok = mutt_addrlist_to_intl(al, &err);
210 if (idna_ok != 0)
211 {
212 mutt_error(_("Bad IDN: '%s'"), err);
213 FREE(&err);
214 }
215 } while (idna_ok != 0);
216
217done:
218 buf_pool_release(&buf);
219 return rc;
220}
221
230static int edit_envelope(struct Envelope *en, SendFlags flags, struct ConfigSubset *sub)
231{
232 int rc = -1;
233 struct Buffer *buf = buf_pool_get();
234 buf_alloc(buf, 8192);
235
236 /* For NNTP posts, prompt for Newsgroups, Followup-To, and X-Comment-To */
237 if (OptNewsSend)
238 {
239 if (en->newsgroups)
240 buf_strcpy(buf, en->newsgroups);
241 else
242 buf_reset(buf);
243
244 if (mw_get_field("Newsgroups: ", buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0)
245 {
246 goto done;
247 }
249
250 if (en->followup_to)
251 buf_strcpy(buf, en->followup_to);
252 else
253 buf_reset(buf);
254
255 const bool c_ask_followup_to = cs_subset_bool(sub, "ask_followup_to");
256 if (c_ask_followup_to && (mw_get_field("Followup-To: ", buf, MUTT_COMP_NO_FLAGS,
257 HC_OTHER, NULL, NULL) != 0))
258 {
259 goto done;
260 }
262
263 if (en->x_comment_to)
264 buf_strcpy(buf, en->x_comment_to);
265 else
266 buf_reset(buf);
267
268 const bool c_x_comment_to = cs_subset_bool(sub, "x_comment_to");
269 const bool c_ask_x_comment_to = cs_subset_bool(sub, "ask_x_comment_to");
270 if (c_x_comment_to && c_ask_x_comment_to &&
271 (mw_get_field("X-Comment-To: ", buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0))
272 {
273 goto done;
274 }
276 }
277 else
278 {
279 /* For email: prompt for To, Cc, Bcc addresses (skipped with fast_reply) */
280 const bool c_fast_reply = cs_subset_bool(sub, "fast_reply");
281 if (TAILQ_EMPTY(&en->to) || !c_fast_reply || (flags & SEND_REVIEW_TO))
282 {
283 if ((mutt_edit_address(&en->to, _("To: "), true) == -1))
284 goto done;
285 }
286
287 const bool c_ask_cc = cs_subset_bool(sub, "ask_cc");
288 if (TAILQ_EMPTY(&en->cc) || !c_fast_reply)
289 {
290 if (c_ask_cc && (mutt_edit_address(&en->cc, _("Cc: "), true) == -1))
291 goto done;
292 }
293
294 const bool c_ask_bcc = cs_subset_bool(sub, "ask_bcc");
295 if (TAILQ_EMPTY(&en->bcc) || !c_fast_reply)
296 {
297 if (c_ask_bcc && (mutt_edit_address(&en->bcc, _("Bcc: "), true) == -1))
298 goto done;
299 }
300
301 if (TAILQ_EMPTY(&en->to) && TAILQ_EMPTY(&en->cc) && TAILQ_EMPTY(&en->bcc))
302 {
303 mutt_warning(_("No recipients specified"));
304 goto done;
305 }
306
307 const bool c_reply_with_xorig = cs_subset_bool(sub, "reply_with_xorig");
308 if (c_reply_with_xorig && (flags & (SEND_REPLY | SEND_LIST_REPLY | SEND_GROUP_REPLY)) &&
309 (mutt_edit_address(&en->from, "From: ", true) == -1))
310 {
311 goto done;
312 }
313 }
314
316 ASSERT(mod_data);
317
318 /* Handle the Subject line: fast_reply skips the prompt if subject already set.
319 * Check user_header for any "Subject:" override from send-hooks. */
320 if (en->subject)
321 {
322 const bool c_fast_reply = cs_subset_bool(sub, "fast_reply");
323 if (c_fast_reply)
324 {
325 rc = 0;
326 goto done;
327 }
328 buf_strcpy(buf, en->subject);
329 }
330 else
331 {
332 const char *p = NULL;
333
334 buf_reset(buf);
335 struct ListNode *uh = NULL;
336 STAILQ_FOREACH(uh, &mod_data->user_header, entries)
337 {
338 size_t plen = mutt_istr_startswith(uh->data, "subject:");
339 if (plen)
340 {
341 p = mutt_str_skip_email_wsp(uh->data + plen);
342 buf_strcpy(buf, p);
343 }
344 }
345 }
346
347 if ((mw_get_field(_("Subject: "), buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0) ||
348 (buf_is_empty(buf) &&
349 (query_quadoption(_("No subject, abort?"), sub, "abort_nosubject") != MUTT_NO)))
350 {
351 mutt_message(_("No subject, aborting"));
352 goto done;
353 }
355 rc = 0;
356
357done:
358 buf_pool_release(&buf);
359 return rc;
360}
361
369static char *nntp_get_header(const char *s)
370{
371 SKIPWS(s);
372 return mutt_str_dup(s);
373}
374
379static void process_user_recips(struct Envelope *env)
380{
382 ASSERT(mod_data);
383
384 struct ListNode *uh = NULL;
385 STAILQ_FOREACH(uh, &mod_data->user_header, entries)
386 {
387 size_t plen;
388 if ((plen = mutt_istr_startswith(uh->data, "to:")))
389 mutt_addrlist_parse(&env->to, uh->data + plen);
390 else if ((plen = mutt_istr_startswith(uh->data, "cc:")))
391 mutt_addrlist_parse(&env->cc, uh->data + plen);
392 else if ((plen = mutt_istr_startswith(uh->data, "bcc:")))
393 mutt_addrlist_parse(&env->bcc, uh->data + plen);
394 else if ((plen = mutt_istr_startswith(uh->data, "newsgroups:")))
395 env->newsgroups = nntp_get_header(uh->data + plen);
396 else if ((plen = mutt_istr_startswith(uh->data, "followup-to:")))
397 env->followup_to = nntp_get_header(uh->data + plen);
398 else if ((plen = mutt_istr_startswith(uh->data, "x-comment-to:")))
399 env->x_comment_to = nntp_get_header(uh->data + plen);
400 }
401}
402
407static void process_user_header(struct Envelope *env)
408{
410 ASSERT(mod_data);
411
412 struct ListNode *uh = NULL;
413 STAILQ_FOREACH(uh, &mod_data->user_header, entries)
414 {
415 size_t plen;
416 if ((plen = mutt_istr_startswith(uh->data, "from:")))
417 {
418 /* User has specified a default From: address. Remove default address */
420 mutt_addrlist_parse(&env->from, uh->data + plen);
421 }
422 else if ((plen = mutt_istr_startswith(uh->data, "reply-to:")))
423 {
425 mutt_addrlist_parse(&env->reply_to, uh->data + plen);
426 }
427 else if ((plen = mutt_istr_startswith(uh->data, "message-id:")))
428 {
429 char *tmp = mutt_extract_message_id(uh->data + plen, NULL);
430 if (mutt_addr_valid_msgid(tmp))
431 {
432 FREE(&env->message_id);
433 env->message_id = tmp;
434 }
435 else
436 {
437 FREE(&tmp);
438 }
439 }
440 else if (!mutt_istr_startswith(uh->data, "to:") &&
441 !mutt_istr_startswith(uh->data, "cc:") &&
442 !mutt_istr_startswith(uh->data, "bcc:") &&
443 !mutt_istr_startswith(uh->data, "newsgroups:") &&
444 !mutt_istr_startswith(uh->data, "followup-to:") &&
445 !mutt_istr_startswith(uh->data, "x-comment-to:") &&
446 !mutt_istr_startswith(uh->data, "supersedes:") &&
447 !mutt_istr_startswith(uh->data, "subject:") &&
448 !mutt_istr_startswith(uh->data, "return-path:"))
449 {
451 }
452 }
453}
454
461void mutt_forward_intro(struct Email *e, FILE *fp, struct ConfigSubset *sub)
462{
463 const struct Expando *c_forward_attribution_intro = cs_subset_expando(sub, "forward_attribution_intro");
464 if (!c_forward_attribution_intro || !fp)
465 return;
466
467 const char *const c_attribution_locale = cs_subset_string(sub, "attribution_locale");
468
469 struct Buffer *buf = buf_pool_get();
470 setlocale(LC_TIME, NONULL(c_attribution_locale));
471 mutt_make_string(buf, -1, c_forward_attribution_intro, NULL, -1, e,
473 setlocale(LC_TIME, "");
474 fputs(buf_string(buf), fp);
475 fputs("\n\n", fp);
476 buf_pool_release(&buf);
477}
478
485void mutt_forward_trailer(struct Email *e, FILE *fp, struct ConfigSubset *sub)
486{
487 const struct Expando *c_forward_attribution_trailer = cs_subset_expando(sub, "forward_attribution_trailer");
488 if (!c_forward_attribution_trailer || !fp)
489 return;
490
491 const char *const c_attribution_locale = cs_subset_string(sub, "attribution_locale");
492
493 struct Buffer *buf = buf_pool_get();
494 setlocale(LC_TIME, NONULL(c_attribution_locale));
495 mutt_make_string(buf, -1, c_forward_attribution_trailer, NULL, -1, e,
497 setlocale(LC_TIME, "");
498 fputc('\n', fp);
499 fputs(buf_string(buf), fp);
500 fputc('\n', fp);
501 buf_pool_release(&buf);
502}
503
513static int include_forward(struct Mailbox *m, struct Email *e, FILE *fp_out,
514 struct ConfigSubset *sub)
515{
516 CopyHeaderFlags chflags = CH_DECODE;
518
519 struct Message *msg = mx_msg_open(m, e);
520 if (!msg)
521 {
522 return -1;
523 }
526
527 const bool c_forward_decode = cs_subset_bool(sub, "forward_decode");
528 if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT) && c_forward_decode)
529 {
530 /* make sure we have the user's passphrase before proceeding... */
532 {
533 mx_msg_close(m, &msg);
534 return -1;
535 }
536 }
537
538 mutt_forward_intro(e, fp_out, sub);
539
540 if (c_forward_decode)
541 {
542 cmflags |= MUTT_CM_DECODE | MUTT_CM_CHARCONV;
543
544 const bool c_weed = cs_subset_bool(sub, "weed");
545 if (c_weed)
546 {
547 chflags |= CH_WEED | CH_REORDER;
548 cmflags |= MUTT_CM_WEED;
549 }
550 }
551
552 const bool c_forward_quote = cs_subset_bool(sub, "forward_quote");
553 if (c_forward_quote)
554 cmflags |= MUTT_CM_PREFIX;
555
556 mutt_copy_message(fp_out, e, msg, cmflags, chflags, 0);
557 mx_msg_close(m, &msg);
558 mutt_forward_trailer(e, fp_out, sub);
559 return 0;
560}
561
572static int inline_forward_attachments(struct Mailbox *m, struct Email *e,
573 struct Body ***plast, enum QuadOption *forwardq,
574 struct ConfigSubset *sub)
575{
576 struct Body **last = *plast;
577 struct Body *body = NULL;
578 struct AttachCtx *actx = NULL;
579 int rc = 0, i;
580
581 struct Message *msg = mx_msg_open(m, e);
582 if (!msg)
583 {
584 return -1;
585 }
586
589
590 actx = MUTT_MEM_CALLOC(1, struct AttachCtx);
591 actx->email = e;
592 actx->fp_root = msg->fp;
593
594 mutt_generate_recvattach_list(actx, actx->email, actx->email->body,
595 actx->fp_root, -1, 0, 0);
596
597 for (i = 0; i < actx->idxlen; i++)
598 {
599 body = actx->idx[i]->body;
600 if ((body->type != TYPE_MULTIPART) && mutt_prefer_as_attachment(body) &&
601 !((body->type == TYPE_APPLICATION) &&
602 (mutt_istr_equal(body->subtype, "pgp-signature") ||
603 mutt_istr_equal(body->subtype, "x-pkcs7-signature") ||
604 mutt_istr_equal(body->subtype, "pkcs7-signature"))))
605 {
606 /* Ask the quadoption only once */
607 if (*forwardq == MUTT_ABORT)
608 {
609 /* L10N: This is the prompt for $forward_attachments.
610 When inline forwarding ($mime_forward answered "no"), this prompts
611 whether to add non-decodable attachments from the original email.
612 Text/plain parts and the like will already be included in the
613 message contents, but other attachment, such as PDF files, will also
614 be added as attachments to the new mail, if this is answered yes. */
615 *forwardq = query_quadoption(_("Forward attachments?"), sub, "forward_attachments");
616 if (*forwardq != MUTT_YES)
617 {
618 if (*forwardq == -1)
619 rc = -1;
620 goto cleanup;
621 }
622 }
623 if (mutt_body_copy(actx->idx[i]->fp, last, body) == -1)
624 {
625 rc = -1;
626 goto cleanup;
627 }
628 last = &((*last)->next);
629 }
630 }
631
632cleanup:
633 *plast = last;
634 mx_msg_close(m, &msg);
635 mutt_actx_free(&actx);
636 return rc;
637}
638
646static void format_attribution(const struct Expando *exp, struct Email *e,
647 FILE *fp_out, struct ConfigSubset *sub)
648{
649 if (!exp || !fp_out)
650 return;
651
652 const char *const c_attribution_locale = cs_subset_string(sub, "attribution_locale");
653
654 struct Buffer *buf = buf_pool_get();
655 setlocale(LC_TIME, NONULL(c_attribution_locale));
656 mutt_make_string(buf, -1, exp, NULL, -1, e, MUTT_FORMAT_NO_FLAGS, NULL);
657 setlocale(LC_TIME, "");
658 fputs(buf_string(buf), fp_out);
659 fputc('\n', fp_out);
660 buf_pool_release(&buf);
661}
662
669void mutt_make_attribution_intro(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
670{
671 format_attribution(cs_subset_expando(sub, "attribution_intro"), e, fp_out, sub);
672}
673
680void mutt_make_attribution_trailer(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
681{
682 format_attribution(cs_subset_expando(sub, "attribution_trailer"), e, fp_out, sub);
683}
684
693static void mutt_make_greeting(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
694{
695 const struct Expando *c_greeting = cs_subset_expando(sub, "greeting");
696 if (!c_greeting || !fp_out)
697 return;
698
699 struct Buffer *buf = buf_pool_get();
700
702 buf->dsize, NeoMutt->env, buf);
703
704 fputs(buf_string(buf), fp_out);
705 fputc('\n', fp_out);
706 buf_pool_release(&buf);
707}
708
718static int include_reply(struct Mailbox *m, struct Email *e, FILE *fp_out,
719 struct ConfigSubset *sub)
720{
722 CopyHeaderFlags chflags = CH_DECODE;
723
724 if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT))
725 {
726 /* make sure we have the user's passphrase before proceeding... */
728 return -1;
729 }
730
731 struct Message *msg = mx_msg_open(m, e);
732 if (!msg)
733 {
734 return -1;
735 }
738
739 mutt_make_attribution_intro(e, fp_out, sub);
740
741 const bool c_header = cs_subset_bool(sub, "header");
742 if (!c_header)
743 cmflags |= MUTT_CM_NOHEADER;
744
745 const bool c_weed = cs_subset_bool(sub, "weed");
746 if (c_weed)
747 {
748 chflags |= CH_WEED | CH_REORDER;
749 cmflags |= MUTT_CM_WEED;
750 }
751
752 mutt_copy_message(fp_out, e, msg, cmflags, chflags, 0);
753 mx_msg_close(m, &msg);
754
755 mutt_make_attribution_trailer(e, fp_out, sub);
756
757 return 0;
758}
759
767static const struct AddressList *choose_default_to(const struct Address *from,
768 const struct Envelope *env,
769 struct ConfigSubset *sub)
770{
771 const bool c_reply_self = cs_subset_bool(sub, "reply_self");
772 if (!c_reply_self && mutt_addr_is_user(from))
773 {
774 /* mail is from the user, assume replying to recipients */
775 return &env->to;
776 }
777 else
778 {
779 return &env->from;
780 }
781}
782
793static int default_to(struct AddressList *to, struct Envelope *env,
794 SendFlags flags, int hmfupto, struct ConfigSubset *sub)
795{
796 const struct Address *from = TAILQ_FIRST(&env->from);
797 const struct Address *reply_to = TAILQ_FIRST(&env->reply_to);
798
799 if (flags && !TAILQ_EMPTY(&env->mail_followup_to) && (hmfupto == MUTT_YES))
800 {
801 mutt_addrlist_copy(to, &env->mail_followup_to, true);
802 return 0;
803 }
804
805 /* Exit now if we're setting up the default Cc list for list-reply
806 * (only set if Mail-Followup-To is present and honoured). */
807 if (flags & SEND_LIST_REPLY)
808 return 0;
809
810 const struct AddressList *default_to = choose_default_to(from, env, sub);
811
812 if (reply_to)
813 {
814 const bool from_is_reply_to = mutt_addr_cmp(from, reply_to);
815 const bool multiple_reply_to = reply_to &&
816 TAILQ_NEXT(TAILQ_FIRST(&env->reply_to), entries);
817
818 const bool c_ignore_list_reply_to = cs_subset_bool(sub, "ignore_list_reply_to");
819 const enum QuadOption c_reply_to = cs_subset_quad(sub, "reply_to");
820 if ((from_is_reply_to && !multiple_reply_to && !reply_to->personal) ||
821 (c_ignore_list_reply_to && mutt_is_mail_list(reply_to) &&
822 (mutt_addrlist_search(&env->to, reply_to) || mutt_addrlist_search(&env->cc, reply_to))))
823 {
824 /* If the Reply-To: address is a mailing list, assume that it was
825 * put there by the mailing list, and use the From: address
826 *
827 * We also take the from header if our correspondent has a reply-to
828 * header which is identical to the electronic mail address given
829 * in his From header, and the reply-to has no display-name. */
830 mutt_addrlist_copy(to, &env->from, false);
831 }
832 else if (!(from_is_reply_to && !multiple_reply_to) && (c_reply_to != MUTT_YES))
833 {
834 char prompt[256] = { 0 };
835 /* There are quite a few mailing lists which set the Reply-To:
836 * header field to the list address, which makes it quite impossible
837 * to send a message to only the sender of the message. This
838 * provides a way to do that. */
839 /* L10N: Asks whether the user respects the reply-to header.
840 If she says no, neomutt will reply to the from header's address instead. */
841 snprintf(prompt, sizeof(prompt), _("Reply to %s%s?"),
842 buf_string(reply_to->mailbox), multiple_reply_to ? ",..." : "");
843 switch (query_quadoption(prompt, sub, "reply_to"))
844 {
845 case MUTT_YES:
846 mutt_addrlist_copy(to, &env->reply_to, false);
847 break;
848
849 case MUTT_NO:
850 mutt_addrlist_copy(to, default_to, false);
851 break;
852
853 default:
854 return -1; /* abort */
855 }
856 }
857 else
858 {
859 mutt_addrlist_copy(to, &env->reply_to, false);
860 }
861 }
862 else
863 {
864 mutt_addrlist_copy(to, default_to, false);
865 }
866
867 return 0;
868}
869
879int mutt_fetch_recips(struct Envelope *out, struct Envelope *in,
880 SendFlags flags, struct ConfigSubset *sub)
881{
882 enum QuadOption hmfupto = MUTT_ABORT;
883 const struct Address *followup_to = TAILQ_FIRST(&in->mail_followup_to);
884
885 if ((flags & (SEND_LIST_REPLY | SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY)) && followup_to)
886 {
887 char prompt[256] = { 0 };
888 snprintf(prompt, sizeof(prompt), _("Follow-up to %s%s?"),
889 buf_string(followup_to->mailbox),
890 TAILQ_NEXT(TAILQ_FIRST(&in->mail_followup_to), entries) ? ",..." : "");
891
892 hmfupto = query_quadoption(prompt, sub, "honor_followup_to");
893 if (hmfupto == MUTT_ABORT)
894 return -1;
895 }
896
897 if (flags & SEND_LIST_REPLY)
898 {
899 add_mailing_lists(&out->to, &in->to, &in->cc);
900
901 if (followup_to && (hmfupto == MUTT_YES) &&
902 (default_to(&out->cc, in, flags & SEND_LIST_REPLY, (hmfupto == MUTT_YES), sub) == MUTT_ABORT))
903 {
904 return -1; /* abort */
905 }
906 }
907 else if (flags & SEND_TO_SENDER)
908 {
909 mutt_addrlist_copy(&out->to, &in->from, false);
910 }
911 else
912 {
913 if (default_to(&out->to, in, flags & (SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY),
914 (hmfupto == MUTT_YES), sub) == -1)
915 {
916 return -1; /* abort */
917 }
918
919 if ((flags & (SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY)) &&
920 (!followup_to || (hmfupto != MUTT_YES)))
921 {
922 /* if(!mutt_addr_is_user(in->to)) */
923 if (flags & SEND_GROUP_REPLY)
924 mutt_addrlist_copy(&out->cc, &in->to, true);
925 else
926 mutt_addrlist_copy(&out->to, &in->to, true);
927 mutt_addrlist_copy(&out->cc, &in->cc, true);
928 }
929 }
930 return 0;
931}
932
938static void add_references(struct ListHead *head, struct Envelope *env)
939{
940 struct ListHead *src = STAILQ_EMPTY(&env->references) ? &env->in_reply_to : &env->references;
941 mutt_list_copy_tail(head, src);
942}
943
949static void add_message_id(struct ListHead *head, struct Envelope *env)
950{
951 if (env->message_id)
952 {
954 }
955}
956
962void mutt_fix_reply_recipients(struct Envelope *env, struct ConfigSubset *sub)
963{
964 const bool c_me_too = cs_subset_bool(sub, "me_too");
965 if (!c_me_too)
966 {
967 const bool c_reply_self = cs_subset_bool(sub, "reply_self");
968
969 /* the order is important here. do the CC: first so that if the
970 * the user is the only recipient, it ends up on the TO: field */
971 remove_user(&env->cc, TAILQ_EMPTY(&env->to));
972 remove_user(&env->to, TAILQ_EMPTY(&env->cc) || c_reply_self);
973 }
974
975 /* the CC field can get cluttered, especially with lists */
978 mutt_addrlist_remove_xrefs(&env->to, &env->cc);
979
980 if (!TAILQ_EMPTY(&env->cc) && TAILQ_EMPTY(&env->to))
981 {
982 TAILQ_SWAP(&env->to, &env->cc, Address, entries);
983 }
984}
985
992void mutt_make_forward_subject(struct Envelope *env, struct Email *e, struct ConfigSubset *sub)
993{
994 if (!env)
995 return;
996
997 const struct Expando *c_forward_format = cs_subset_expando(sub, "forward_format");
998
999 struct Buffer *buf = buf_pool_get();
1000 /* set the default subject for the message. */
1001 mutt_make_string(buf, -1, c_forward_format, NULL, -1, e, MUTT_FORMAT_NO_FLAGS, NULL);
1003 buf_pool_release(&buf);
1004}
1005
1012void mutt_make_misc_reply_headers(struct Envelope *env, struct Envelope *env_cur,
1013 struct ConfigSubset *sub)
1014{
1015 if (!env || !env_cur)
1016 return;
1017
1018 /* This takes precedence over a subject that might have
1019 * been taken from a List-Post header. Is that correct? */
1020 if (env_cur->real_subj)
1021 {
1022 char *subj = NULL;
1023 mutt_str_asprintf(&subj, "Re: %s", env_cur->real_subj);
1024 mutt_env_set_subject(env, subj);
1025 FREE(&subj);
1026 }
1027 else if (!env->subject)
1028 {
1029 const char *const c_empty_subject = cs_subset_string(sub, "empty_subject");
1030 mutt_env_set_subject(env, c_empty_subject);
1031 }
1032}
1033
1040void mutt_add_to_reference_headers(struct Envelope *env, struct Envelope *env_cur,
1041 struct ConfigSubset *sub)
1042{
1043 add_references(&env->references, env_cur);
1044 add_message_id(&env->references, env_cur);
1045 add_message_id(&env->in_reply_to, env_cur);
1046
1047 const bool c_x_comment_to = cs_subset_bool(sub, "x_comment_to");
1048 if (OptNewsSend && c_x_comment_to && !TAILQ_EMPTY(&env_cur->from))
1050}
1051
1058static void make_reference_headers(struct EmailArray *ea, struct Envelope *env,
1059 struct ConfigSubset *sub)
1060{
1061 if (!ea || !env || ARRAY_EMPTY(ea))
1062 return;
1063
1064 struct Email **ep = NULL;
1065 ARRAY_FOREACH(ep, ea)
1066 {
1067 struct Email *e = *ep;
1069 }
1070
1071 /* if there's more than entry in In-Reply-To (i.e. message has multiple
1072 * parents), don't generate a References: header as it's discouraged by
1073 * RFC2822, sect. 3.6.4 */
1074 if ((ARRAY_SIZE(ea) > 1) && !STAILQ_EMPTY(&env->in_reply_to) &&
1076 {
1078 }
1079}
1080
1090static int envelope_defaults(struct Envelope *env, struct EmailArray *ea,
1091 SendFlags flags, struct ConfigSubset *sub)
1092{
1093 if (!ea || ARRAY_EMPTY(ea))
1094 return -1;
1095
1096 struct Email *e_cur = *ARRAY_GET(ea, 0);
1097 bool single = (ARRAY_SIZE(ea) == 1);
1098
1099 struct Envelope *env_cur = e_cur->env;
1100 if (!env_cur)
1101 return -1;
1102
1103 if (flags & (SEND_REPLY | SEND_TO_SENDER))
1104 {
1105 if ((flags & SEND_NEWS))
1106 {
1107 /* in case followup set Newsgroups: with Followup-To: if it present */
1108 if (!env->newsgroups && !mutt_istr_equal(env_cur->followup_to, "poster"))
1109 {
1110 env->newsgroups = mutt_str_dup(env_cur->followup_to);
1111 }
1112 }
1113 else if (!single)
1114 {
1115 struct Email **ep = NULL;
1116 ARRAY_FOREACH(ep, ea)
1117 {
1118 struct Email *e = *ep;
1119 if (mutt_fetch_recips(env, e->env, flags, sub) == -1)
1120 return -1;
1121 }
1122 }
1123 else if (mutt_fetch_recips(env, env_cur, flags, sub) == -1)
1124 {
1125 return -1;
1126 }
1127
1128 if ((flags & SEND_LIST_REPLY) && TAILQ_EMPTY(&env->to))
1129 {
1130 mutt_error(_("No mailing lists found"));
1131 return -1;
1132 }
1133
1134 if (flags & SEND_REPLY)
1135 {
1136 mutt_make_misc_reply_headers(env, env_cur, sub);
1137 make_reference_headers(ea, env, sub);
1138 }
1139 }
1140 else if (flags & SEND_FORWARD)
1141 {
1142 mutt_make_forward_subject(env, e_cur, sub);
1143
1144 const bool c_forward_references = cs_subset_bool(sub, "forward_references");
1145 if (c_forward_references)
1146 make_reference_headers(ea, env, sub);
1147 }
1148
1149 return 0;
1150}
1151
1163static int generate_body(FILE *fp_tmp, struct Email *e, SendFlags flags,
1164 struct Mailbox *m, struct EmailArray *ea, struct ConfigSubset *sub)
1165{
1166 /* An EmailList is required for replying and forwarding */
1167 if (!ea && (flags & (SEND_REPLY | SEND_FORWARD)))
1168 return -1;
1169
1170 /* Handle reply: optionally include quoted original message(s) */
1171 if (flags & SEND_REPLY)
1172 {
1173 enum QuadOption ans = query_quadoption(_("Include message in reply?"), sub, "include");
1174 if (ans == MUTT_ABORT)
1175 return -1;
1176
1177 if (ans == MUTT_YES)
1178 {
1179 mutt_message(_("Including quoted message..."));
1180 struct Email **ep = NULL;
1181 size_t count = ARRAY_SIZE(ea) - 1;
1182 ARRAY_FOREACH(ep, ea)
1183 {
1184 if (include_reply(m, *ep, fp_tmp, sub) == -1)
1185 {
1186 mutt_error(_("Could not include all requested messages"));
1187 return -1;
1188 }
1189 if (ARRAY_FOREACH_IDX_ep < count)
1190 {
1191 fputc('\n', fp_tmp);
1192 }
1193 }
1194 }
1195 }
1196 else if (flags & SEND_FORWARD)
1197 {
1198 /* Handle forward: either as message/rfc822 MIME attachment
1199 * or inline with optional attachment forwarding */
1200 enum QuadOption ans = query_quadoption(_("Forward as attachment?"), sub, "mime_forward");
1201 if (ans == MUTT_YES)
1202 {
1203 struct Body *last = e->body;
1204
1205 mutt_message(_("Preparing forwarded message..."));
1206
1207 while (last && last->next)
1208 last = last->next;
1209
1210 struct Email **ep = NULL;
1211 ARRAY_FOREACH(ep, ea)
1212 {
1213 struct Body *tmp = mutt_make_message_attach(m, *ep, false, sub);
1214 if (last)
1215 {
1216 last->next = tmp;
1217 last = tmp;
1218 }
1219 else
1220 {
1221 last = tmp;
1222 e->body = tmp;
1223 }
1224 }
1225 }
1226 else if (ans != MUTT_ABORT)
1227 {
1228 enum QuadOption forwardq = MUTT_ABORT;
1229 struct Body **last = NULL;
1230
1231 const bool c_forward_decode = cs_subset_bool(sub, "forward_decode");
1232 const enum QuadOption c_forward_attachments = cs_subset_quad(sub, "forward_attachments");
1233 if (c_forward_decode && (c_forward_attachments != MUTT_NO))
1234 {
1235 last = &e->body;
1236 while (*last)
1237 last = &((*last)->next);
1238 }
1239
1240 struct Email **ep = NULL;
1241 ARRAY_FOREACH(ep, ea)
1242 {
1243 struct Email *e_cur = *ep;
1244 include_forward(m, e_cur, fp_tmp, sub);
1245 if (c_forward_decode && (c_forward_attachments != MUTT_NO))
1246 {
1247 if (inline_forward_attachments(m, e_cur, &last, &forwardq, sub) != 0)
1248 return -1;
1249 }
1250 }
1251 }
1252 else
1253 {
1254 return -1;
1255 }
1256 }
1257 else if (((WithCrypto & APPLICATION_PGP) != 0) && (flags & SEND_KEY))
1258 {
1259 /* Attach the user's PGP public key to the message */
1260 struct Body *b = NULL;
1261
1262 if (((WithCrypto & APPLICATION_PGP) != 0) && !(b = crypt_pgp_make_key_attachment()))
1263 {
1264 return -1;
1265 }
1266
1267 b->next = e->body;
1268 e->body = b;
1269 }
1270
1272
1273 return 0;
1274}
1275
1281void mutt_set_followup_to(struct Envelope *env, struct ConfigSubset *sub)
1282{
1283 /* Only generate the Mail-Followup-To if the user has requested it, and
1284 * it hasn't already been set */
1285
1286 const bool c_followup_to = cs_subset_bool(sub, "followup_to");
1287 if (!c_followup_to)
1288 return;
1289 if (OptNewsSend)
1290 {
1291 if (!env->followup_to && env->newsgroups && (strrchr(env->newsgroups, ',')))
1292 env->followup_to = mutt_str_dup(env->newsgroups);
1293 return;
1294 }
1295
1296 if (TAILQ_EMPTY(&env->mail_followup_to))
1297 {
1298 if (mutt_is_list_recipient(false, env))
1299 {
1300 /* this message goes to known mailing lists, so create a proper
1301 * mail-followup-to header */
1302
1303 mutt_addrlist_copy(&env->mail_followup_to, &env->to, false);
1304 mutt_addrlist_copy(&env->mail_followup_to, &env->cc, true);
1305 }
1306
1307 /* remove ourselves from the mail-followup-to header */
1308 remove_user(&env->mail_followup_to, false);
1309
1310 /* If we are not subscribed to any of the lists in question, re-add
1311 * ourselves to the mail-followup-to header. The mail-followup-to header
1312 * generated is a no-op with group-reply, but makes sure list-reply has the
1313 * desired effect. */
1314
1315 if (!TAILQ_EMPTY(&env->mail_followup_to) &&
1317 {
1318 struct AddressList *al = NULL;
1319 if (!TAILQ_EMPTY(&env->reply_to))
1320 al = &env->reply_to;
1321 else if (!TAILQ_EMPTY(&env->from))
1322 al = &env->from;
1323
1324 if (al)
1325 {
1326 struct Address *a = NULL;
1327 TAILQ_FOREACH_REVERSE(a, al, AddressList, entries)
1328 {
1330 }
1331 }
1332 else
1333 {
1335 }
1336 }
1337
1339 }
1340}
1341
1352static void set_reverse_name(struct AddressList *al, struct Envelope *env,
1353 struct ConfigSubset *sub)
1354{
1355 struct Address *a = NULL;
1356 if (TAILQ_EMPTY(al))
1357 {
1358 TAILQ_FOREACH(a, &env->to, entries)
1359 {
1360 if (mutt_addr_is_user(a))
1361 {
1363 break;
1364 }
1365 }
1366 }
1367
1368 if (TAILQ_EMPTY(al))
1369 {
1370 TAILQ_FOREACH(a, &env->cc, entries)
1371 {
1372 if (mutt_addr_is_user(a))
1373 {
1375 break;
1376 }
1377 }
1378 }
1379
1380 if (TAILQ_EMPTY(al))
1381 {
1382 struct Address *from = TAILQ_FIRST(&env->from);
1383 if (from && mutt_addr_is_user(from))
1384 {
1386 }
1387 }
1388
1389 if (!TAILQ_EMPTY(al))
1390 {
1391 /* when $reverse_real_name is not set, clear the personal name so that it
1392 * may be set via a reply- or send-hook. */
1393
1394 const bool c_reverse_real_name = cs_subset_bool(sub, "reverse_real_name");
1395 if (!c_reverse_real_name)
1396 FREE(&TAILQ_FIRST(al)->personal);
1397 }
1398}
1399
1406{
1407 /* Note: We let $from override $real_name here.
1408 * Is this the right thing to do?
1409 */
1410
1411 const struct Address *c_from = cs_subset_address(sub, "from");
1412 if (c_from)
1413 {
1414 return mutt_addr_copy(c_from);
1415 }
1416
1417 char domain[1024] = { 0 };
1418 const char *mailbox = NeoMutt->username;
1419 const bool c_use_domain = cs_subset_bool(sub, "use_domain");
1420 if (c_use_domain)
1421 {
1422 snprintf(domain, sizeof(domain), "%s@%s", NONULL(NeoMutt->username),
1423 NONULL(mutt_fqdn(true, sub)));
1424 mailbox = domain;
1425 }
1426
1427 return mutt_addr_create(NULL, mailbox);
1428}
1429
1438static int invoke_mta(struct Mailbox *m, struct Email *e, struct ConfigSubset *sub)
1439{
1440 struct Buffer *tempfile = NULL;
1441 int rc = -1;
1442
1443 /* Write out the message in MIME form. */
1444 tempfile = buf_pool_get();
1445 buf_mktemp(tempfile);
1446 FILE *fp_tmp = mutt_file_fopen(buf_string(tempfile), "w");
1447 if (!fp_tmp)
1448 goto cleanup;
1449
1450 const bool c_write_bcc = cs_subset_bool(sub, "write_bcc");
1451 const char *const c_smtp_url = cs_subset_string(sub, "smtp_url");
1452 if (c_smtp_url)
1453 cs_subset_str_native_set(sub, "write_bcc", false, NULL);
1454
1456 false, mutt_should_hide_protected_subject(e), sub);
1457
1458 cs_subset_str_native_set(sub, "write_bcc", c_write_bcc, NULL);
1459
1460 fputc('\n', fp_tmp); /* tie off the header. */
1461
1462 if ((mutt_write_mime_body(e->body, fp_tmp, sub) == -1))
1463 goto cleanup;
1464
1465 if (mutt_file_fclose(&fp_tmp) != 0)
1466 {
1467 mutt_perror("%s", buf_string(tempfile));
1468 unlink(buf_string(tempfile));
1469 goto cleanup;
1470 }
1471
1472 if (OptNewsSend)
1473 goto sendmail;
1474
1475 if (c_smtp_url)
1476 {
1477 rc = mutt_smtp_send(&e->env->from, &e->env->to, &e->env->cc, &e->env->bcc,
1478 buf_string(tempfile), (e->body->encoding == ENC_8BIT), sub);
1479 goto cleanup;
1480 }
1481
1482sendmail:
1483 rc = mutt_invoke_sendmail(m, &e->env->from, &e->env->to, &e->env->cc, &e->env->bcc,
1484 buf_string(tempfile), (e->body->encoding == ENC_8BIT), sub);
1485cleanup:
1486 if (fp_tmp)
1487 {
1488 mutt_file_fclose(&fp_tmp);
1489 unlink(buf_string(tempfile));
1490 }
1491 buf_pool_release(&tempfile);
1492 return rc;
1493}
1494
1501void mutt_encode_descriptions(struct Body *b, bool recurse, struct ConfigSubset *sub)
1502{
1503 const struct Slist *const c_send_charset = cs_subset_slist(sub, "send_charset");
1504 for (struct Body *t = b; t; t = t->next)
1505 {
1506 if (t->description)
1507 {
1508 rfc2047_encode(&t->description, NULL, sizeof("Content-Description:"), c_send_charset);
1509 }
1510 if (recurse && t->parts)
1511 mutt_encode_descriptions(t->parts, recurse, sub);
1512 }
1513}
1514
1519static void decode_descriptions(struct Body *b)
1520{
1521 for (struct Body *t = b; t; t = t->next)
1522 {
1523 if (t->description)
1524 {
1525 rfc2047_decode(&t->description);
1526 }
1527 if (t->parts)
1528 decode_descriptions(t->parts);
1529 }
1530}
1531
1536static void fix_end_of_file(const char *data)
1537{
1538 FILE *fp = mutt_file_fopen(data, "a+");
1539 if (!fp)
1540 return;
1541
1542 if ((mutt_file_get_size_fp(fp) > 0) && mutt_file_seek(fp, -1, SEEK_END))
1543 {
1544 int c = fgetc(fp);
1545 if (c != '\n')
1546 fputc('\n', fp);
1547 }
1548 mutt_file_fclose(&fp);
1549}
1550
1561int mutt_resend_message(FILE *fp, struct Mailbox *m, struct Email *e_cur,
1562 struct ConfigSubset *sub)
1563{
1564 struct Email *e_new = email_new();
1565
1566 if (mutt_prepare_template(fp, m, e_new, e_cur, true) < 0)
1567 {
1568 email_free(&e_new);
1569 return -1;
1570 }
1571
1572 if (WithCrypto)
1573 {
1574 /* mutt_prepare_template doesn't always flip on an application bit.
1575 * so fix that here */
1576 if (!(e_new->security & (APPLICATION_SMIME | APPLICATION_PGP)))
1577 {
1578 const bool c_smime_is_default = cs_subset_bool(sub, "smime_is_default");
1579 if (((WithCrypto & APPLICATION_SMIME) != 0) && c_smime_is_default)
1580 e_new->security |= APPLICATION_SMIME;
1581 else if (WithCrypto & APPLICATION_PGP)
1582 e_new->security |= APPLICATION_PGP;
1583 else
1584 e_new->security |= APPLICATION_SMIME;
1585 }
1586
1587 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(sub, "crypt_opportunistic_encrypt");
1588 if (c_crypt_opportunistic_encrypt)
1589 {
1590 e_new->security |= SEC_OPPENCRYPT;
1592 }
1593 }
1594
1595 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
1596 ARRAY_ADD(&ea, e_cur);
1597 int rc = mutt_send_message(SEND_RESEND, e_new, NULL, m, &ea, sub);
1598 ARRAY_FREE(&ea);
1599
1600 return rc;
1601}
1602
1610static bool is_reply(struct Email *reply, struct Email *orig)
1611{
1612 if (!reply || !reply->env || !orig || !orig->env)
1613 return false;
1614 return mutt_list_find(&orig->env->references, reply->env->message_id) ||
1615 mutt_list_find(&orig->env->in_reply_to, reply->env->message_id);
1616}
1617
1629static bool search_attach_keyword(char *filename, struct ConfigSubset *sub)
1630{
1631 const struct Regex *c_abort_noattach_regex = cs_subset_regex(sub, "abort_noattach_regex");
1632 const struct Regex *c_quote_regex = cs_subset_regex(sub, "quote_regex");
1633
1634 /* Search for the regex in `$abort_noattach_regex` within a file */
1635 if (!c_abort_noattach_regex || !c_abort_noattach_regex->regex ||
1636 !c_quote_regex || !c_quote_regex->regex)
1637 {
1638 return false;
1639 }
1640
1641 FILE *fp_att = mutt_file_fopen(filename, "r");
1642 if (!fp_att)
1643 return false;
1644
1645 char *inputline = MUTT_MEM_MALLOC(1024, char);
1646 bool found = false;
1647 while (!feof(fp_att) && fgets(inputline, 1024, fp_att))
1648 {
1649 if (!mutt_is_quote_line(inputline, NULL) &&
1650 mutt_regex_match(c_abort_noattach_regex, inputline))
1651 {
1652 found = true;
1653 break;
1654 }
1655 }
1656 FREE(&inputline);
1657 mutt_file_fclose(&fp_att);
1658 return found;
1659}
1660
1674static int save_fcc(struct Mailbox *m, struct Email *e, struct Buffer *fcc,
1675 struct Body *clear_content, char *pgpkeylist,
1676 SendFlags flags, char **finalpath, struct ConfigSubset *sub)
1677{
1678 int rc = 0;
1679 struct Body *save_content = NULL;
1680
1681 expand_path(fcc, false);
1682
1683 if (buf_is_empty(fcc) || mutt_str_equal("/dev/null", buf_string(fcc)))
1684 return rc;
1685
1686 struct Body *tmpbody = e->body;
1687 struct Body *save_sig = NULL;
1688 struct Body *save_parts = NULL;
1689
1690 const bool c_fcc_before_send = cs_subset_bool(sub, "fcc_before_send");
1691 /* Before sending, we don't allow message manipulation because it
1692 * will break message signatures. This is especially complicated by
1693 * Protected Headers. */
1694 if (!c_fcc_before_send)
1695 {
1696 const bool c_fcc_clear = cs_subset_bool(sub, "fcc_clear");
1697 if ((WithCrypto != 0) &&
1698 (e->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT)) && c_fcc_clear)
1699 {
1700 e->body = clear_content;
1703 mutt_param_delete(&e->body->parameter, "protected-headers");
1704 }
1705
1706 const enum QuadOption c_fcc_attach = cs_subset_quad(sub, "fcc_attach");
1707
1708 /* check to see if the user wants copies of all attachments */
1709 bool save_atts = true;
1710 if (e->body->type == TYPE_MULTIPART)
1711 {
1712 /* In batch mode, save attachments if the quadoption is yes or ask-yes */
1713 if (flags & SEND_BATCH)
1714 {
1715 if ((c_fcc_attach == MUTT_NO) || (c_fcc_attach == MUTT_ASKNO))
1716 save_atts = false;
1717 }
1718 else if (query_quadoption(_("Save attachments in Fcc?"), sub, "fcc_attach") != MUTT_YES)
1719 {
1720 save_atts = false;
1721 }
1722 }
1723 if (!save_atts)
1724 {
1725 if ((WithCrypto != 0) && (e->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT)) &&
1726 (mutt_str_equal(e->body->subtype, "encrypted") ||
1727 mutt_str_equal(e->body->subtype, "signed")))
1728 {
1729 if ((clear_content->type == TYPE_MULTIPART) &&
1730 (query_quadoption(_("Save attachments in Fcc?"), sub, "fcc_attach") != MUTT_YES))
1731 {
1732 if (!(e->security & SEC_ENCRYPT) && (e->security & SEC_SIGN))
1733 {
1734 /* save initial signature and attachments */
1735 save_sig = e->body->parts->next;
1736 save_parts = clear_content->parts->next;
1737 }
1738
1739 /* this means writing only the main part */
1740 e->body = clear_content->parts;
1741
1742 if (mutt_protect(e, pgpkeylist, false) == -1)
1743 {
1744 /* we can't do much about it at this point, so
1745 * fallback to saving the whole thing to fcc */
1746 e->body = tmpbody;
1747 save_sig = NULL;
1748 goto full_fcc;
1749 }
1750
1751 save_content = e->body;
1752 }
1753 }
1754 else
1755 {
1756 if (query_quadoption(_("Save attachments in Fcc?"), sub, "fcc_attach") != MUTT_YES)
1757 e->body = e->body->parts;
1758 }
1759 }
1760 }
1761
1762full_fcc:
1763 if (e->body)
1764 {
1765 /* update received time so that when storing to a mbox-style folder
1766 * the From_ line contains the current time instead of when the
1767 * message was first postponed. */
1768 e->received = mutt_date_now();
1769 rc = mutt_write_multiple_fcc(buf_string(fcc), e, NULL, false, NULL, finalpath, sub);
1770 if (rc && (flags & SEND_BATCH))
1771 {
1772 /* Printed when an Fcc in batch mode fails. */
1773 mutt_error(_("Warning: Fcc to %s failed"), buf_string(fcc));
1774 return rc;
1775 }
1776
1777 while (rc && !(flags & SEND_BATCH))
1778 {
1780 int choice = mw_multi_choice(
1781 /* L10N: Called when saving to $record or Fcc failed after sending.
1782 (r)etry tries the same mailbox again.
1783 alternate (m)ailbox prompts for a different mailbox to try.
1784 (s)kip aborts saving. */
1785 _("Fcc failed. (r)etry, alternate (m)ailbox, or (s)kip?"),
1786 /* L10N: These correspond to the "Fcc failed" multi-choice prompt
1787 (r)etry, alternate (m)ailbox, or (s)kip.
1788 Any similarity to famous leaders of the FSF is coincidental. */
1789 _("rms"));
1790 switch (choice)
1791 {
1792 case 2: /* alternate (m)ailbox */
1793 /* L10N: This is the prompt to enter an "alternate (m)ailbox" when the
1794 initial Fcc fails. */
1795 rc = mw_enter_fname(_("Fcc mailbox"), fcc, true, m, false, NULL, NULL,
1797 if ((rc == -1) || buf_is_empty(fcc))
1798 {
1799 rc = 0;
1800 break;
1801 }
1803
1804 case 1: /* (r)etry */
1805 rc = mutt_write_multiple_fcc(buf_string(fcc), e, NULL, false, NULL, finalpath, sub);
1806 break;
1807
1808 case -1: /* abort */
1809 case 3: /* (s)kip */
1810 rc = 0;
1811 break;
1812 }
1813 }
1814 }
1815
1816 if (!c_fcc_before_send)
1817 {
1818 e->body = tmpbody;
1819
1820 if ((WithCrypto != 0) && save_sig)
1821 {
1822 /* cleanup the second signature structures */
1823 if (save_content->parts)
1824 {
1825 mutt_body_free(&save_content->parts->next);
1826 save_content->parts = NULL;
1827 }
1828 mutt_body_free(&save_content);
1829
1830 /* restore old signature and attachments */
1831 e->body->parts->next = save_sig;
1832 e->body->parts->parts->next = save_parts;
1833 }
1834 else if ((WithCrypto != 0) && save_content)
1835 {
1836 /* destroy the new encrypted body. */
1837 mutt_body_free(&save_content);
1838 }
1839 }
1840
1841 return 0;
1842}
1843
1854static int postpone_message(struct Email *e_post, struct Email *e_cur,
1855 const char *fcc, SendFlags flags, struct ConfigSubset *sub)
1856{
1857 char *pgpkeylist = NULL;
1858 const char *encrypt_as = NULL;
1859 struct Body *clear_content = NULL;
1860
1861 const char *const c_postponed = cs_subset_string(sub, "postponed");
1862 if (!c_postponed)
1863 {
1864 mutt_error(_("Can't postpone. $postponed is unset"));
1865 return -1;
1866 }
1867
1868 if (e_post->body->next)
1869 e_post->body = mutt_make_multipart(e_post->body);
1870
1871 mutt_encode_descriptions(e_post->body, true, sub);
1872
1873 const bool c_postpone_encrypt = cs_subset_bool(sub, "postpone_encrypt");
1874 if ((WithCrypto != 0) && c_postpone_encrypt &&
1875 (e_post->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)))
1876 {
1877 if (((WithCrypto & APPLICATION_PGP) != 0) && (e_post->security & APPLICATION_PGP))
1878 {
1879 const char *const c_pgp_default_key = cs_subset_string(sub, "pgp_default_key");
1880 encrypt_as = c_pgp_default_key;
1881 }
1882 else if (((WithCrypto & APPLICATION_SMIME) != 0) && (e_post->security & APPLICATION_SMIME))
1883 {
1884 const char *const c_smime_default_key = cs_subset_string(sub, "smime_default_key");
1885 encrypt_as = c_smime_default_key;
1886 }
1887 if (!encrypt_as)
1888 {
1889 const char *const c_postpone_encrypt_as = cs_subset_string(sub, "postpone_encrypt_as");
1890 encrypt_as = c_postpone_encrypt_as;
1891 }
1892
1893#ifdef USE_AUTOCRYPT
1894 if (e_post->security & SEC_AUTOCRYPT)
1895 {
1897 {
1898 if (mutt_istr_equal(e_post->body->subtype, "mixed"))
1899 e_post->body = mutt_remove_multipart(e_post->body);
1900 decode_descriptions(e_post->body);
1901 mutt_error(_("Error encrypting message. Check your crypt settings."));
1902 return -1;
1903 }
1905 encrypt_as = ac_mod_data->autocrypt_default_key;
1906 }
1907#endif
1908
1909 if (encrypt_as)
1910 {
1911 pgpkeylist = mutt_str_dup(encrypt_as);
1912 clear_content = e_post->body;
1913 if (mutt_protect(e_post, pgpkeylist, true) == -1)
1914 {
1915 FREE(&pgpkeylist);
1916 if (mutt_istr_equal(e_post->body->subtype, "mixed"))
1917 e_post->body = mutt_remove_multipart(e_post->body);
1918 decode_descriptions(e_post->body);
1919 mutt_error(_("Error encrypting message. Check your crypt settings."));
1920 return -1;
1921 }
1922
1923 FREE(&pgpkeylist);
1924
1925 mutt_encode_descriptions(e_post->body, false, sub);
1926 }
1927 }
1928
1929 /* make sure the message is written to the right part of a maildir
1930 * postponed folder. */
1931 e_post->read = false;
1932 e_post->old = false;
1933
1934 mutt_prepare_envelope(e_post->env, false, sub);
1935 mutt_env_to_intl(e_post->env, NULL, NULL); /* Handle bad IDNAs the next time. */
1936
1937 if (mutt_write_fcc(NONULL(c_postponed), e_post,
1938 (e_cur && (flags & SEND_REPLY)) ? e_cur->env->message_id : NULL,
1939 true, fcc, NULL, sub) < 0)
1940 {
1941 if (clear_content)
1942 {
1943 mutt_body_free(&e_post->body);
1944 e_post->body = clear_content;
1945 }
1946 mutt_env_free(&e_post->body->mime_headers); /* protected headers */
1947 mutt_param_delete(&e_post->body->parameter, "protected-headers");
1948 if (mutt_istr_equal(e_post->body->subtype, "mixed"))
1949 e_post->body = mutt_remove_multipart(e_post->body);
1950 decode_descriptions(e_post->body);
1952 return -1;
1953 }
1954
1956
1957 if (clear_content)
1958 mutt_body_free(&clear_content);
1959
1960 return 0;
1961}
1962
1969static bool is_text_plain(const struct Body *b)
1970{
1971 return (b->type == TYPE_TEXT) && mutt_istr_equal(b->subtype, "plain");
1972}
1973
1980static bool abort_for_missing_attachments(const struct Body *b, struct ConfigSubset *sub)
1981{
1982 const enum QuadOption c_abort_noattach = cs_subset_quad(sub, "abort_noattach");
1983
1984 if (c_abort_noattach == MUTT_NO)
1985 return false;
1986
1987 if (b->next)
1988 return false;
1989
1990 bool has_keyword = false;
1991
1992 /* search text/plain parts, whether they are main or alternative parts */
1993 if (is_text_plain(b))
1994 {
1995 has_keyword |= search_attach_keyword(b->filename, sub);
1996 }
1997 else
1998 {
1999 for (b = b->parts; b; b = b->next)
2000 {
2001 if (is_text_plain(b))
2002 {
2003 has_keyword |= search_attach_keyword(b->filename, sub);
2004 }
2005 }
2006 }
2007
2008 if (!has_keyword)
2009 return false;
2010
2011 if (c_abort_noattach == MUTT_YES)
2012 {
2013 mutt_error(_("Message contains text matching \"$abort_noattach_regex\". Not sending."));
2014 return true;
2015 }
2016
2017 return query_quadoption(_("No attachments, cancel sending?"), sub, "abort_noattach") != MUTT_NO;
2018}
2019
2032int mutt_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile,
2033 struct Mailbox *m, struct EmailArray *ea, struct ConfigSubset *sub)
2034{
2035 struct Buffer *fcc = buf_pool_get(); /* where to copy this message */
2036 FILE *fp_tmp = NULL;
2037 struct Body *pbody = NULL;
2038 int i;
2039 bool free_clear_content = false;
2040
2041 struct Body *clear_content = NULL;
2042 char *pgpkeylist = NULL;
2043 /* save current value of "pgp_sign_as" and "smime_default_key" */
2044 char *pgp_sign_as = NULL;
2045 char *smime_sign_as = NULL;
2046 const char *tag = NULL;
2047 char *err = NULL;
2048 const char *ctype = NULL;
2049 char *finalpath = NULL;
2050 struct Email *e_cur = NULL;
2051
2052 if (ea && (ARRAY_SIZE(ea) == 1))
2053 e_cur = *ARRAY_GET(ea, 0);
2054
2055 int rc = -1;
2056
2057 if (flags & SEND_NEWS)
2058 OptNewsSend = true;
2059 else
2060 OptNewsSend = false;
2061
2062 const enum QuadOption c_recall = cs_subset_quad(sub, "recall");
2063
2064 if (!flags && !e_templ && (c_recall != MUTT_NO) && mutt_num_postponed(m, true))
2065 {
2066 /* If the user is composing a new message, check to see if there
2067 * are any postponed messages first. */
2068 enum QuadOption ans = query_quadoption(_("Recall postponed message?"), sub, "recall");
2069 if (ans == MUTT_ABORT)
2070 return rc;
2071
2072 if (ans == MUTT_YES)
2073 flags |= SEND_POSTPONED;
2074 }
2075
2076 if (flags & SEND_POSTPONED)
2077 {
2079 {
2080 const char *const c_pgp_sign_as = cs_subset_string(sub, "pgp_sign_as");
2081 pgp_sign_as = mutt_str_dup(c_pgp_sign_as);
2082 }
2084 {
2085 const char *const c_smime_sign_as = cs_subset_string(sub, "smime_sign_as");
2086 smime_sign_as = mutt_str_dup(c_smime_sign_as);
2087 }
2088 }
2089
2090 /* Delay expansion of aliases until absolutely necessary--shouldn't
2091 * be necessary unless we are prompting the user or about to execute a
2092 * send-hook. */
2093
2094 if (!e_templ)
2095 {
2096 e_templ = email_new();
2097
2098 if (flags == SEND_POSTPONED)
2099 {
2100 rc = mutt_get_postponed(m, e_templ, &e_cur, fcc);
2101 if (rc < 0)
2102 {
2103 flags = SEND_POSTPONED;
2104 goto cleanup;
2105 }
2106 flags = rc;
2107 /* If postponed message is a news article, it have
2108 * a "Newsgroups:" header line, then set appropriate flag. */
2109 if (e_templ->env->newsgroups)
2110 {
2111 flags |= SEND_NEWS;
2112 OptNewsSend = true;
2113 }
2114 else
2115 {
2116 flags &= ~SEND_NEWS;
2117 OptNewsSend = false;
2118 }
2119 }
2120
2121 if (flags & (SEND_POSTPONED | SEND_RESEND))
2122 {
2123 struct Body *b = e_templ->body;
2124 while (b->parts)
2125 b = b->parts;
2126 fp_tmp = mutt_file_fopen(b->filename, "a+");
2127 if (!fp_tmp)
2128 {
2129 mutt_perror("%s", b->filename);
2130 goto cleanup;
2131 }
2132 }
2133
2134 if (!e_templ->env)
2135 e_templ->env = mutt_env_new();
2136 }
2137
2138 /* Parse and use an eventual list-post header */
2139 if ((flags & SEND_LIST_REPLY) && e_cur && e_cur->env && e_cur->env->list_post)
2140 {
2141 /* Use any list-post header as a template */
2142 mutt_parse_mailto(e_templ->env, NULL, e_cur->env->list_post);
2143 /* We don't let them set the sender's address. */
2144 mutt_addrlist_clear(&e_templ->env->from);
2145 }
2146
2147 if (!(flags & (SEND_KEY | SEND_POSTPONED | SEND_RESEND)))
2148 {
2149 /* When SEND_DRAFT_FILE is set, the caller has already
2150 * created the "parent" body structure. */
2151 if (!(flags & SEND_DRAFT_FILE))
2152 {
2153 pbody = mutt_body_new();
2154 pbody->next = e_templ->body; /* don't kill command-line attachments */
2155 e_templ->body = pbody;
2156
2157 const char *const c_content_type = cs_subset_string(sub, "content_type");
2158 ctype = c_content_type;
2159 if (!ctype)
2160 ctype = "text/plain";
2161 mutt_parse_content_type(ctype, e_templ->body);
2162 e_templ->body->unlink = true;
2163 e_templ->body->use_disp = false;
2164 e_templ->body->disposition = DISP_INLINE;
2165
2166 if (tempfile)
2167 {
2168 fp_tmp = mutt_file_fopen(tempfile, "a+");
2169 e_templ->body->filename = mutt_str_dup(tempfile);
2170 if (flags & SEND_NO_FREE_HEADER)
2171 e_templ->body->unlink = false;
2172 }
2173 else
2174 {
2175 struct Buffer *buf = buf_pool_get();
2176 buf_mktemp_draft(buf);
2177 fp_tmp = mutt_file_fopen(buf_string(buf), "w+");
2178 e_templ->body->filename = buf_strdup(buf);
2179 buf_pool_release(&buf);
2180 }
2181 }
2182 else
2183 {
2184 struct Body *b = e_templ->body;
2185 while (b->parts)
2186 b = b->parts;
2187 fp_tmp = mutt_file_fopen(b->filename, "a+");
2188 }
2189
2190 if (!fp_tmp)
2191 {
2192 mutt_debug(LL_DEBUG1, "can't create tempfile %s (errno=%d)\n",
2193 e_templ->body->filename, errno);
2194 mutt_perror("%s", e_templ->body->filename);
2195 goto cleanup;
2196 }
2197 }
2198
2199 const bool c_reverse_name = cs_subset_bool(sub, "reverse_name");
2200 /* this is handled here so that the user can match ~f in send-hook */
2201 if (e_cur && c_reverse_name && !(flags & (SEND_POSTPONED | SEND_RESEND)))
2202 {
2203 /* We shouldn't have to worry about alias expansion here since we are
2204 * either replying to a real or postponed message, therefore no aliases
2205 * should exist since the user has not had the opportunity to add
2206 * addresses to the list. We just have to ensure the postponed messages
2207 * have their aliases expanded. */
2208
2209 if (!TAILQ_EMPTY(&e_templ->env->from))
2210 {
2211 mutt_debug(LL_DEBUG5, "e_templ->env->from before set_reverse_name: %s\n",
2212 buf_string(TAILQ_FIRST(&e_templ->env->from)->mailbox));
2213 mutt_addrlist_clear(&e_templ->env->from);
2214 }
2215 set_reverse_name(&e_templ->env->from, e_cur->env, sub);
2216 }
2217
2218 const bool c_reply_with_xorig = cs_subset_bool(sub, "reply_with_xorig");
2219 if (e_cur && c_reply_with_xorig && !(flags & (SEND_POSTPONED | SEND_RESEND | SEND_FORWARD)))
2220 {
2221 /* We shouldn't have to worry about freeing 'e_templ->env->from' before
2222 * setting it here since this code will only execute when doing some
2223 * sort of reply. The pointer will only be set when using the -H command
2224 * line option.
2225 *
2226 * If there is already a from address recorded in 'e_templ->env->from',
2227 * then it theoretically comes from `$reverse_name` handling, and we don't use
2228 * the 'X-Original-To header'. */
2229 if (!TAILQ_EMPTY(&e_cur->env->x_original_to) && TAILQ_EMPTY(&e_templ->env->from))
2230 {
2231 mutt_addrlist_copy(&e_templ->env->from, &e_cur->env->x_original_to, false);
2232 mutt_debug(LL_DEBUG5, "e_templ->env->from extracted from X-Original-To: header: %s\n",
2233 buf_string(TAILQ_FIRST(&e_templ->env->from)->mailbox));
2234 }
2235 }
2236
2237 if (!e_templ->env->message_id)
2238 e_templ->env->message_id = msgid_generate();
2239
2240 const bool c_resume_draft_files = cs_subset_bool(sub, "resume_draft_files");
2241 if (!(flags & (SEND_POSTPONED | SEND_RESEND)) &&
2242 !((flags & SEND_DRAFT_FILE) && c_resume_draft_files))
2243 {
2244 if ((flags & (SEND_REPLY | SEND_FORWARD | SEND_TO_SENDER)) &&
2245 (envelope_defaults(e_templ->env, ea, flags, sub) == -1))
2246 {
2247 goto cleanup;
2248 }
2249
2250 const bool c_hdrs = cs_subset_bool(sub, "hdrs");
2251 if (c_hdrs)
2252 process_user_recips(e_templ->env);
2253
2254 /* Expand aliases and remove duplicates/crossrefs */
2255 mutt_expand_aliases_env(e_templ->env);
2256
2257 if (flags & SEND_REPLY)
2258 mutt_fix_reply_recipients(e_templ->env, sub);
2259
2260 if ((flags & SEND_NEWS) && (m && m->type == MUTT_NNTP) && !e_templ->env->newsgroups)
2261 {
2262 e_templ->env->newsgroups = mutt_str_dup(((struct NntpMboxData *) m->mdata)->group);
2263 }
2264
2265 const bool c_auto_edit = cs_subset_bool(sub, "auto_edit");
2266 const bool c_edit_headers = cs_subset_bool(sub, "edit_headers");
2267 const bool c_fast_reply = cs_subset_bool(sub, "fast_reply");
2268 if (!(flags & SEND_BATCH) && !(c_auto_edit && c_edit_headers) &&
2269 !((flags & SEND_REPLY) && c_fast_reply))
2270 {
2271 if (edit_envelope(e_templ->env, flags, sub) == -1)
2272 goto cleanup;
2273 }
2274
2275 /* the from address must be set here regardless of whether or not
2276 * $use_from is set so that the '~P' (from you) operator in send-hook
2277 * patterns will work. if $use_from is unset, the from address is killed
2278 * after send-hooks are evaluated */
2279
2280 const bool killfrom = TAILQ_EMPTY(&e_templ->env->from);
2281 if (killfrom)
2282 {
2284 }
2285
2286 if ((flags & SEND_REPLY) && e_cur)
2287 {
2288 /* change setting based upon message we are replying to */
2290
2291 /* set the replied flag for the message we are generating so that the
2292 * user can use ~Q in a send-hook to know when reply-hook's are also
2293 * being used. */
2294 e_templ->replied = true;
2295 }
2296
2297 /* change settings based upon recipients */
2298
2299 exec_message_hook(NULL, e_templ, CMD_SEND_HOOK);
2300
2301 /* Unset the replied flag from the message we are composing since it is
2302 * no longer required. This is done here because the FCC'd copy of
2303 * this message was erroneously get the 'R'eplied flag when stored in
2304 * a maildir-style mailbox. */
2305 e_templ->replied = false;
2306
2307 /* $use_from and/or $from might have changed in a send-hook */
2308 if (killfrom)
2309 {
2310 mutt_addrlist_clear(&e_templ->env->from);
2311
2312 const bool c_use_from = cs_subset_bool(sub, "use_from");
2313 if (c_use_from && !(flags & (SEND_POSTPONED | SEND_RESEND)))
2315 }
2316
2317 if (c_hdrs)
2318 process_user_header(e_templ->env);
2319
2320 if ((flags & SEND_BATCH) && !(flags & SEND_CONSUMED_STDIN))
2321 {
2322 if (mutt_file_copy_stream(stdin, fp_tmp) < 0)
2323 {
2324 mutt_error(_("Error sending message"));
2325 goto cleanup;
2326 }
2327 }
2328
2329 if (!(flags & SEND_BATCH))
2330 mutt_make_greeting(e_templ, fp_tmp, sub);
2331
2332 const bool c_sig_on_top = cs_subset_bool(sub, "sig_on_top");
2333 const char *const c_editor = cs_subset_string(sub, "editor");
2334 if (c_sig_on_top && !(flags & (SEND_KEY | SEND_BATCH)) && c_editor)
2335 {
2336 append_signature(fp_tmp, sub);
2337 }
2338
2339 /* include replies/forwarded messages, unless we are given a template */
2340 if (!tempfile && (m || !(flags & (SEND_REPLY | SEND_FORWARD))) &&
2341 (generate_body(fp_tmp, e_templ, flags, m, ea, sub) == -1))
2342 {
2343 goto cleanup;
2344 }
2345
2346 if (!c_sig_on_top && !(flags & (SEND_KEY | SEND_BATCH)) && c_editor)
2347 {
2348 append_signature(fp_tmp, sub);
2349 }
2350 }
2351
2352 /* Only set format=flowed for new messages. postponed/resent/draftfiles
2353 * should respect the original email.
2354 *
2355 * This is set here so that send-hook can be used to turn the option on. */
2356 if (!(flags & (SEND_KEY | SEND_POSTPONED | SEND_RESEND | SEND_DRAFT_FILE)))
2357 {
2358 const bool c_text_flowed = cs_subset_bool(sub, "text_flowed");
2359 if (c_text_flowed && is_text_plain(e_templ->body))
2360 {
2361 mutt_param_set(&e_templ->body->parameter, "format", "flowed");
2362 }
2363 }
2364
2365 /* This hook is even called for postponed messages, and can, e.g., be used
2366 * for setting the editor, the sendmail path, or the envelope sender. */
2367 exec_message_hook(NULL, e_templ, CMD_SEND2_HOOK);
2368
2369 /* wait until now to set the real name portion of our return address so
2370 * that $real_name can be set in a send-hook */
2371 {
2372 struct Address *from = TAILQ_FIRST(&e_templ->env->from);
2373 if (from && !from->personal && !(flags & (SEND_RESEND | SEND_POSTPONED)))
2374 {
2375 const char *const c_real_name = cs_subset_string(sub, "real_name");
2376 if (c_real_name)
2377 from->personal = buf_new(c_real_name);
2378 }
2379 }
2380
2381 if (!(((WithCrypto & APPLICATION_PGP) != 0) && (flags & SEND_KEY)))
2382 mutt_file_fclose(&fp_tmp);
2383
2384 if (!(flags & SEND_BATCH))
2385 {
2386 struct stat st = { 0 };
2387 time_t mtime;
2388 struct Body *b = e_templ->body;
2389 while (b->parts)
2390 b = b->parts;
2391 mtime = mutt_file_decrease_mtime(b->filename, NULL);
2392 if (mtime == (time_t) -1)
2393 {
2394 mutt_perror("%s", b->filename);
2395 goto cleanup;
2396 }
2397
2398 mutt_update_encoding(b, sub);
2399
2400 const bool c_edit_headers = cs_subset_bool(sub, "edit_headers");
2401 const bool c_auto_edit = cs_subset_bool(sub, "auto_edit");
2402
2403 /* Select whether or not the user's editor should be called now. We
2404 * don't want to do this when:
2405 * 1) we are sending a key/cert
2406 * 2) we are forwarding a message and the user doesn't want to edit it.
2407 * This is controlled by the quadoption $forward_edit. However, if
2408 * both $edit_headers and $auto_edit are set, we want to ignore the
2409 * setting of $forward_edit because the user probably needs to add the
2410 * recipients. */
2411 if (!(flags & SEND_KEY) &&
2412 (((flags & SEND_FORWARD) == 0) || (c_edit_headers && c_auto_edit) ||
2413 (query_quadoption(_("Edit forwarded message?"), sub, "forward_edit") == MUTT_YES)))
2414 {
2415 /* If the this isn't a text message, look for a mailcap edit command */
2416 const char *const c_editor = cs_subset_string(sub, "editor");
2417 b = e_templ->body;
2418 while (b->parts)
2419 b = b->parts;
2420 if (mutt_needs_mailcap(b))
2421 {
2422 if (!mutt_edit_attachment(b))
2423 goto cleanup;
2424 }
2425 else if (c_edit_headers)
2426 {
2427 mutt_env_to_local(e_templ->env);
2428 mutt_edit_headers(c_editor, b->filename, e_templ, fcc);
2429 mutt_env_to_intl(e_templ->env, NULL, NULL);
2430 }
2431 else
2432 {
2433 mutt_edit_file(c_editor, b->filename);
2434 if (stat(b->filename, &st) == 0)
2435 {
2436 if (mtime != st.st_mtime)
2438 }
2439 else
2440 {
2441 mutt_perror("%s", b->filename);
2442 }
2443 }
2444
2445 exec_message_hook(NULL, e_templ, CMD_SEND2_HOOK);
2446 }
2447
2449 {
2450 if (stat(e_templ->body->filename, &st) == 0)
2451 {
2452 /* if the file was not modified, bail out now */
2453 if ((mtime == st.st_mtime) && !e_templ->body->next &&
2454 (query_quadoption(_("Abort unmodified message?"), sub, "abort_unmodified") == MUTT_YES))
2455 {
2456 mutt_message(_("Aborted unmodified message"));
2457 goto cleanup;
2458 }
2459 }
2460 else
2461 {
2462 mutt_perror("%s", e_templ->body->filename);
2463 }
2464 }
2465 }
2466
2467 /* Set the message security unless:
2468 * 1) crypto support is not enabled (WithCrypto==0)
2469 * 2) pgp: header field was present during message editing with $edit_headers (e_templ->security != 0)
2470 * 3) we are resending a message
2471 * 4) we are recalling a postponed message (don't override the user's saved settings)
2472 * 5) we are in batch mode
2473 * But 3, 4, and 5, can be overridden with '-C' in the command line (flags & SEND_CLI_CRYPTO)
2474 *
2475 * This is done after allowing the user to edit the message so that security
2476 * settings can be configured with send2-hook and $edit_headers. */
2477 if ((WithCrypto != 0) && (e_templ->security == 0) &&
2478 ((flags & SEND_CLI_CRYPTO) || !(flags & (SEND_BATCH | SEND_POSTPONED | SEND_RESEND))))
2479 {
2480 bool c_autocrypt = false;
2481 bool c_autocrypt_reply = false;
2482
2483#ifdef USE_AUTOCRYPT
2484 c_autocrypt = cs_subset_bool(sub, "autocrypt");
2485 c_autocrypt_reply = cs_subset_bool(sub, "autocrypt_reply");
2486#endif
2487
2488 if (c_autocrypt && c_autocrypt_reply && e_cur && (e_cur->security & SEC_AUTOCRYPT))
2489 {
2491 }
2492 else
2493 {
2494 const bool c_crypt_auto_sign = cs_subset_bool(sub, "crypt_auto_sign");
2495 const bool c_crypt_auto_encrypt = cs_subset_bool(sub, "crypt_auto_encrypt");
2496 const bool c_crypt_reply_encrypt = cs_subset_bool(sub, "crypt_reply_encrypt");
2497 const bool c_crypt_reply_sign = cs_subset_bool(sub, "crypt_reply_sign");
2498 const bool c_crypt_reply_sign_encrypted = cs_subset_bool(sub, "crypt_reply_sign_encrypted");
2499
2500 if (c_crypt_auto_sign)
2501 e_templ->security |= SEC_SIGN;
2502 if (c_crypt_auto_encrypt)
2503 e_templ->security |= SEC_ENCRYPT;
2504 if (c_crypt_reply_encrypt && e_cur && (e_cur->security & SEC_ENCRYPT))
2505 e_templ->security |= SEC_ENCRYPT;
2506 if (c_crypt_reply_sign && e_cur && (e_cur->security & SEC_SIGN))
2507 e_templ->security |= SEC_SIGN;
2508 if (c_crypt_reply_sign_encrypted && e_cur && (e_cur->security & SEC_ENCRYPT))
2509 e_templ->security |= SEC_SIGN;
2510
2511 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(sub, "crypt_opportunistic_encrypt");
2512
2513 if (((WithCrypto & APPLICATION_PGP) != 0) &&
2514 ((e_templ->security & (SEC_ENCRYPT | SEC_SIGN)) || c_crypt_opportunistic_encrypt))
2515 {
2516 const bool c_pgp_auto_inline = cs_subset_bool(sub, "pgp_auto_inline");
2517 const bool c_pgp_reply_inline = cs_subset_bool(sub, "pgp_reply_inline");
2518
2519 if (c_pgp_auto_inline)
2520 e_templ->security |= SEC_INLINE;
2521 if (c_pgp_reply_inline && e_cur && (e_cur->security & SEC_INLINE))
2522 e_templ->security |= SEC_INLINE;
2523 }
2524 }
2525
2526 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(sub, "crypt_opportunistic_encrypt");
2527
2528 if (e_templ->security || c_crypt_opportunistic_encrypt)
2529 {
2530 const bool c_crypt_auto_pgp = cs_subset_bool(sub, "crypt_auto_pgp");
2531 const bool c_crypt_auto_smime = cs_subset_bool(sub, "crypt_auto_smime");
2532
2533 /* When replying / forwarding, use the original message's
2534 * crypto system. According to the documentation,
2535 * smime_is_default should be disregarded here.
2536 *
2537 * Problem: At least with forwarding, this doesn't really
2538 * make much sense. Should we have an option to completely
2539 * disable individual mechanisms at run-time? */
2540 if (e_cur)
2541 {
2542 if (((WithCrypto & APPLICATION_PGP) != 0) && c_crypt_auto_pgp &&
2543 (e_cur->security & APPLICATION_PGP))
2544 {
2545 e_templ->security |= APPLICATION_PGP;
2546 }
2547 else if (((WithCrypto & APPLICATION_SMIME) != 0) &&
2548 c_crypt_auto_smime && (e_cur->security & APPLICATION_SMIME))
2549 {
2550 e_templ->security |= APPLICATION_SMIME;
2551 }
2552 }
2553
2554 const bool c_smime_is_default = cs_subset_bool(sub, "smime_is_default");
2555
2556 /* No crypto mechanism selected? Use availability + smime_is_default
2557 * for the decision. */
2558 if (!(e_templ->security & (APPLICATION_SMIME | APPLICATION_PGP)))
2559 {
2560 if (((WithCrypto & APPLICATION_SMIME) != 0) && c_crypt_auto_smime && c_smime_is_default)
2561 {
2562 e_templ->security |= APPLICATION_SMIME;
2563 }
2564 else if (((WithCrypto & APPLICATION_PGP) != 0) && c_crypt_auto_pgp)
2565 {
2566 e_templ->security |= APPLICATION_PGP;
2567 }
2568 else if (((WithCrypto & APPLICATION_SMIME) != 0) && c_crypt_auto_smime)
2569 {
2570 e_templ->security |= APPLICATION_SMIME;
2571 }
2572 }
2573 }
2574
2575 /* opportunistic encrypt relies on SMIME or PGP already being selected */
2576 if (c_crypt_opportunistic_encrypt)
2577 {
2578 /* If something has already enabled encryption, e.g. `$crypt_auto_encrypt`
2579 * or `$crypt_reply_encrypt`, then don't enable opportunistic encrypt for
2580 * the message. */
2581 if (!(e_templ->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)))
2582 {
2583 e_templ->security |= SEC_OPPENCRYPT;
2585 }
2586 }
2587
2588 /* No permissible mechanisms found. Don't sign or encrypt. */
2589 if (!(e_templ->security & (APPLICATION_SMIME | APPLICATION_PGP)))
2590 e_templ->security = SEC_NO_FLAGS;
2591 }
2592
2593 /* Deal with the corner case where the crypto module backend is not available.
2594 * This can happen if configured without PGP/SMIME and with GPGME, but
2595 * $crypt_use_gpgme is unset. */
2596 if (e_templ->security && !crypt_has_module_backend(e_templ->security))
2597 {
2598 mutt_error(_("No crypto backend configured. Disabling message security setting."));
2599 e_templ->security = SEC_NO_FLAGS;
2600 }
2601
2602 /* specify a default fcc. if we are in batchmode, only save a copy of
2603 * the message if the value of $copy is yes or ask-yes */
2604
2605 const enum QuadOption c_copy = cs_subset_quad(sub, "copy");
2606
2607 if (buf_is_empty(fcc) && !(flags & SEND_POSTPONED_FCC) &&
2608 (!(flags & SEND_BATCH) || (c_copy & 0x1)))
2609 {
2610 /* set the default FCC */
2611 const bool killfrom = TAILQ_EMPTY(&e_templ->env->from);
2612 if (killfrom)
2613 {
2615 }
2616 mutt_select_fcc(fcc, e_templ);
2617 if (killfrom)
2618 {
2619 mutt_addrlist_clear(&e_templ->env->from);
2620 }
2621 }
2622
2623 mutt_rfc3676_space_stuff(e_templ);
2624
2625 mutt_update_encoding(e_templ->body, sub);
2626
2627 if (!(flags & SEND_BATCH))
2628 {
2629 main_loop:
2630
2631 pretty_mailbox(fcc);
2632 i = dlg_compose(e_templ, fcc,
2633 ((flags & SEND_NO_FREE_HEADER) ? MUTT_COMPOSE_NOFREEHEADER : 0), sub);
2634 if (i == -1)
2635 {
2636 /* abort */
2637 if (flags & SEND_NEWS)
2638 mutt_message(_("Article not posted"));
2639 else
2640 mutt_message(_("Mail not sent"));
2641 goto cleanup;
2642 }
2643 else if (i == 1)
2644 {
2645 if (postpone_message(e_templ, e_cur, buf_string(fcc), flags, sub) != 0)
2646 goto main_loop;
2647 mutt_message(_("Message postponed"));
2648 rc = 1;
2649 goto cleanup;
2650 }
2651 }
2652
2653 if (!(flags & SEND_NEWS))
2654 {
2655 if ((mutt_addrlist_count_recips(&e_templ->env->to) == 0) &&
2656 (mutt_addrlist_count_recips(&e_templ->env->cc) == 0) &&
2657 (mutt_addrlist_count_recips(&e_templ->env->bcc) == 0))
2658 {
2659 if (flags & SEND_BATCH)
2660 {
2661 puts(_("No recipients specified"));
2662 goto cleanup;
2663 }
2664
2665 mutt_warning(_("No recipients specified"));
2666 goto main_loop;
2667 }
2668 }
2669
2670 if (mutt_env_to_intl(e_templ->env, &tag, &err))
2671 {
2672 mutt_error(_("Bad IDN in '%s': '%s'"), tag, err);
2673 FREE(&err);
2674 if (flags & SEND_BATCH)
2675 goto cleanup;
2676 goto main_loop;
2677 }
2678
2679 const enum QuadOption c_abort_nosubject = cs_subset_quad(sub, "abort_nosubject");
2680
2681 if (!e_templ->env->subject && !(flags & SEND_BATCH) &&
2682 (query_quadoption(_("No subject, abort sending?"), sub, "abort_nosubject") != MUTT_NO))
2683 {
2684 /* if the abort is automatic, print an error message */
2685 if (c_abort_nosubject == MUTT_YES)
2686 mutt_error(_("No subject specified"));
2687 goto main_loop;
2688 }
2689
2690 if ((flags & SEND_NEWS) && !e_templ->env->subject)
2691 {
2692 mutt_error(_("No subject specified"));
2693 goto main_loop;
2694 }
2695
2696 if ((flags & SEND_NEWS) && !e_templ->env->newsgroups)
2697 {
2698 mutt_error(_("No newsgroup specified"));
2699 goto main_loop;
2700 }
2701
2702 if (!(flags & SEND_BATCH) && abort_for_missing_attachments(e_templ->body, sub))
2703 {
2704 goto main_loop;
2705 }
2706
2707 const bool c_confirm_empty_to = cs_subset_bool(sub, "confirm_empty_to");
2708 if (c_confirm_empty_to && TAILQ_EMPTY(&e_templ->env->to) &&
2709 (query_yesorno(_("No recipients specified in To. Send anyway?"), MUTT_NO) == MUTT_NO))
2710 {
2711 goto main_loop;
2712 }
2713
2714 if (e_templ->body->next)
2715 e_templ->body = mutt_make_multipart(e_templ->body);
2716
2717 /* Ok, we need to do it this way instead of handling all fcc stuff in
2718 * one place in order to avoid going to main_loop with encoded "env"
2719 * in case of error. Ugh. */
2720
2721 mutt_encode_descriptions(e_templ->body, true, sub);
2722
2723 /* Make sure that clear_content and free_clear_content are
2724 * properly initialized -- we may visit this particular place in
2725 * the code multiple times, including after a failed call to
2726 * mutt_protect(). */
2727
2728 clear_content = NULL;
2729 free_clear_content = false;
2730
2731 if (WithCrypto)
2732 {
2733 if (e_templ->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT))
2734 {
2735 /* save the decrypted attachments */
2736 clear_content = e_templ->body;
2737
2738 if ((crypt_get_keys(e_templ, &pgpkeylist, false) == -1) ||
2739 (mutt_protect(e_templ, pgpkeylist, false) == -1))
2740 {
2741 if (mutt_istr_equal(e_templ->body->subtype, "mixed"))
2742 e_templ->body = mutt_remove_multipart(e_templ->body);
2743
2744 FREE(&pgpkeylist);
2745
2746 decode_descriptions(e_templ->body);
2747
2748 if (flags & SEND_BATCH)
2749 {
2750 mutt_message(_("Missing encryption key; mail not sent"));
2751 rc = -1;
2752 goto cleanup;
2753 }
2754
2755 goto main_loop;
2756 }
2757 mutt_encode_descriptions(e_templ->body, false, sub);
2758 }
2759
2760 /* at this point, e_templ->body is one of the following three things:
2761 * - multipart/signed. In this case, clear_content is a child
2762 * - multipart/encrypted. In this case, clear_content exists independently
2763 * - application/pgp. In this case, clear_content exists independently
2764 * - something else. In this case, it's the same as clear_content */
2765
2766 /* This is ugly -- lack of "reporting back" from mutt_protect(). */
2767
2768 if (clear_content && (e_templ->body != clear_content) &&
2769 (e_templ->body->parts != clear_content))
2770 free_clear_content = true;
2771 }
2772
2773 if (OptGui)
2774 mutt_message(_("Sending message..."));
2775
2776 mutt_prepare_envelope(e_templ->env, true, sub);
2777
2778 const bool c_fcc_before_send = cs_subset_bool(sub, "fcc_before_send");
2779 if (c_fcc_before_send)
2780 {
2781 if (save_fcc(m, e_templ, fcc, clear_content, pgpkeylist, flags, &finalpath, sub) &&
2782 (flags & SEND_BATCH))
2783 {
2784 /* In batch mode with $fcc_before_send set, abort sending if Fcc fails. */
2785 puts(_("Fcc failed. Aborting sending."));
2786 goto cleanup;
2787 }
2788 }
2789
2790 i = invoke_mta(m, e_templ, sub);
2791 if (i < 0)
2792 {
2793 if (!(flags & SEND_BATCH))
2794 {
2795 if (!WithCrypto)
2796 ; // do nothing
2797 else if ((e_templ->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)) ||
2798 ((e_templ->security & SEC_SIGN) && (e_templ->body->type == TYPE_APPLICATION)))
2799 {
2800 if (e_templ->body != clear_content)
2801 {
2802 mutt_body_free(&e_templ->body); /* destroy PGP data */
2803 e_templ->body = clear_content; /* restore clear text. */
2804 }
2805 }
2806 else if ((e_templ->security & SEC_SIGN) && (e_templ->body->type == TYPE_MULTIPART))
2807 {
2808 mutt_body_free(&e_templ->body->parts->next); /* destroy sig */
2809 if (mutt_istr_equal(e_templ->body->subtype, "mixed") ||
2810 mutt_istr_equal(e_templ->body->subtype, "signed"))
2811 {
2812 e_templ->body = mutt_remove_multipart(e_templ->body);
2813 }
2814 }
2815
2816 FREE(&pgpkeylist);
2817 mutt_env_free(&e_templ->body->mime_headers); /* protected headers */
2818 mutt_param_delete(&e_templ->body->parameter, "protected-headers");
2819 if (mutt_istr_equal(e_templ->body->subtype, "mixed"))
2820 e_templ->body = mutt_remove_multipart(e_templ->body);
2821 decode_descriptions(e_templ->body);
2822 mutt_unprepare_envelope(e_templ->env);
2823 FREE(&finalpath);
2824 goto main_loop;
2825 }
2826 else
2827 {
2828 puts(_("Could not send the message"));
2829 goto cleanup;
2830 }
2831 }
2832
2833 if (!c_fcc_before_send)
2834 save_fcc(m, e_templ, fcc, clear_content, pgpkeylist, flags, &finalpath, sub);
2835
2836 if (OptGui)
2837 {
2838 mutt_message((i != 0) ? _("Sending in background") :
2839 (flags & SEND_NEWS) ? _("Article posted") :
2840 _("Mail sent"));
2841#ifdef USE_NOTMUCH
2842 const bool c_nm_record = cs_subset_bool(sub, "nm_record");
2843 if (c_nm_record)
2844 nm_record_message(m, finalpath, e_cur);
2845#endif
2846 mutt_sleep(0);
2847 }
2848
2849 if (WithCrypto)
2850 FREE(&pgpkeylist);
2851
2852 if ((WithCrypto != 0) && free_clear_content)
2853 mutt_body_free(&clear_content);
2854
2855 /* set 'replied' flag only if the user didn't change/remove
2856 * In-Reply-To: and References: headers during edit */
2857 if (flags & SEND_REPLY)
2858 {
2859 if (!(flags & SEND_POSTPONED) && m)
2860 {
2861 struct Email **ep = NULL;
2862 ARRAY_FOREACH(ep, ea)
2863 {
2864 struct Email *e = *ep;
2865 mutt_set_flag(m, e, MUTT_REPLIED, is_reply(e, e_templ), true);
2866 }
2867 }
2868 }
2869
2870 rc = 0;
2871
2872cleanup:
2873 buf_pool_release(&fcc);
2874
2875 if (flags & SEND_POSTPONED)
2876 {
2878 {
2879 cs_subset_str_string_set(sub, "pgp_sign_as", pgp_sign_as, NULL);
2880 FREE(&pgp_sign_as);
2881 }
2883 {
2884 cs_subset_str_string_set(sub, "smime_sign_as", smime_sign_as, NULL);
2885 FREE(&smime_sign_as);
2886 }
2887 }
2888
2889 mutt_file_fclose(&fp_tmp);
2890 if (!(flags & SEND_NO_FREE_HEADER))
2891 email_free(&e_templ);
2892
2893 FREE(&finalpath);
2894 return rc;
2895}
2896
2907static bool send_simple_email(struct Mailbox *m, struct EmailArray *ea,
2908 const char *mailto, const char *subj, const char *body)
2909{
2910 struct Email *e = email_new();
2911
2912 /* envelope */
2913 e->env = mutt_env_new();
2914 mutt_parse_mailto(e->env, NULL, mailto);
2915 if (!e->env->subject)
2916 {
2917 mutt_env_set_subject(e->env, subj);
2918 }
2919 if (TAILQ_EMPTY(&e->env->to) && !mutt_addrlist_parse(&e->env->to, NULL))
2920 {
2921 mutt_warning(_("No recipient specified"));
2922 }
2923
2924 /* body */
2925 e->body = mutt_body_new();
2926 char ctype[] = "text/plain";
2927 mutt_parse_content_type(ctype, e->body);
2928
2929 struct Buffer *tempfile = buf_pool_get();
2930 buf_mktemp_draft(tempfile);
2931 if (body)
2932 {
2933 FILE *fp = mutt_file_fopen(buf_string(tempfile), "w+");
2934 if (!fp)
2935 {
2936 email_free(&e);
2937 buf_pool_release(&tempfile);
2938 return false;
2939 }
2940 fprintf(fp, "%s\n", body);
2941 mutt_file_fclose(&fp);
2942 }
2943 e->body->filename = buf_strdup(tempfile);
2944 e->body->unlink = true;
2945 buf_pool_release(&tempfile);
2946
2947 const int rc = mutt_send_message(SEND_DRAFT_FILE, e, NULL, m, ea, NeoMutt->sub);
2948 return rc >= 0;
2949}
2950
2958bool mutt_send_list_subscribe(struct Mailbox *m, struct Email *e)
2959{
2960 if (!e || !e->env)
2961 {
2962 return false;
2963 }
2964
2965 const char *mailto = e->env->list_subscribe;
2966 if (!mailto)
2967 {
2968 mutt_warning(_("No List-Subscribe header found"));
2969 return false;
2970 }
2971
2972 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
2973 ARRAY_ADD(&ea, e);
2974 bool rc = send_simple_email(m, &ea, mailto, "Subscribe", "subscribe");
2975 ARRAY_FREE(&ea);
2976
2977 return rc;
2978}
2979
2987bool mutt_send_list_unsubscribe(struct Mailbox *m, struct Email *e)
2988{
2989 if (!e || !e->env)
2990 {
2991 return false;
2992 }
2993
2994 const char *mailto = e->env->list_unsubscribe;
2995 if (!mailto)
2996 {
2997 mutt_warning(_("No List-Unsubscribe header found"));
2998 return false;
2999 }
3000
3001 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
3002 ARRAY_ADD(&ea, e);
3003 bool rc = send_simple_email(m, &ea, mailto, "Unsubscribe", "unsubscribe");
3004 ARRAY_FREE(&ea);
3005
3006 return rc;
3007}
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:774
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition address.c:1469
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:801
bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
Compare two e-mail addresses.
Definition address.c:901
void mutt_addrlist_append(struct AddressList *al, struct Address *a)
Append an Address to an AddressList.
Definition address.c:1489
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition address.c:1387
size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
Write an Address to a buffer.
Definition address.c:1215
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition address.c:649
struct Address * mutt_addr_copy(const struct Address *addr)
Copy the real address.
Definition address.c:754
void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
Remove cross-references.
Definition address.c:1442
int mutt_addrlist_count_recips(const struct AddressList *al)
Count the number of Addresses with valid recipients.
Definition address.c:881
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:1500
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition address.c:1302
bool mutt_addrlist_search(const struct AddressList *haystack, const struct Address *needle)
Search for an e-mail address in a list.
Definition address.c:918
void mutt_addrlist_dedupe(struct AddressList *al)
Remove duplicate addresses.
Definition address.c:1406
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.
Autocrypt end-to-end encryption.
Autocrypt private Module data.
int mutt_autocrypt_set_sign_as_default_key(struct Email *e)
Set the Autocrypt default key for signing.
Definition autocrypt.c:716
Select a Mailbox from a list.
#define MUTT_SEL_NO_FLAGS
No flags are set.
Definition lib.h:55
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:55
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
void crypt_opportunistic_encrypt(struct Email *e)
Can all recipients be determined.
Definition crypt.c:1050
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:1105
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:966
bool crypt_has_module_backend(SecurityFlags type)
Is there a crypto backend for a given type?
Definition cryptglue.c:183
struct Body * crypt_pgp_make_key_attachment(void)
Wrapper for CryptModuleSpecs::pgp_make_key_attachment()
Definition cryptglue.c:317
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition curs_lib.c:118
bool mutt_is_quote_line(char *line, regmatch_t *pmatch)
Is a line of message text a quote?
Definition display.c:324
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:824
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:430
bool mutt_parse_mailto(struct Envelope *env, char **body, const char *src)
Parse a mailto:// url.
Definition parse.c:1723
char * mutt_extract_message_id(const char *s, size_t *len)
Find a Message-ID.
Definition parse.c:368
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:224
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition file.c:1432
time_t mutt_file_decrease_mtime(const char *fp, struct stat *st)
Decrease a file's modification time by 1 second.
Definition file.c:898
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition file.c:648
#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:46
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:238
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:467
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
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:1882
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:256
void exec_message_hook(struct Mailbox *m, struct Email *e, enum CommandId id)
Perform a message hook.
Definition exec.c:137
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_AUTOCRYPT
ModuleAutocrypt, Autocrypt
Definition module_api.h:50
@ 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:133
struct Body * mutt_make_multipart(struct Body *b)
Create a multipart email.
Definition multipart.c:107
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:228
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:677
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:808
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition string.c:613
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:665
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:787
void pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition muttlib.c:428
bool mutt_needs_mailcap(struct Body *b)
Does this type need a mailcap entry do display.
Definition muttlib.c:368
void expand_path(struct Buffer *buf, bool regex)
Create the canonical path.
Definition muttlib.c:122
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition muttlib.c:645
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:94
#define SEC_AUTOCRYPT
(Autocrypt) Message will be, or was Autocrypt encrypt+signed
Definition lib.h:96
#define SEC_OPPENCRYPT
Opportunistic encrypt mode.
Definition lib.h:95
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition lib.h:99
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition lib.h:100
#define SEC_NO_FLAGS
No flags are set.
Definition lib.h:86
#define SEC_ENCRYPT
Email is encrypted.
Definition lib.h:87
#define WithCrypto
Definition lib.h:125
#define SEC_AUTOCRYPT_OVERRIDE
(Autocrypt) Indicates manual set/unset of encryption
Definition lib.h:97
#define SEC_SIGN
Email is signed.
Definition lib.h:88
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:665
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:1956
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:63
void mutt_update_num_postponed(void)
Force the update of the number of postponed messages.
Definition postpone.c:171
int mutt_get_postponed(struct Mailbox *m_cur, struct Email *hdr, struct Email **cur, struct Buffer *fcc)
Recall a postponed message.
Definition postpone.c:665
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:490
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:384
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition question.c:329
#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:636
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition rfc2047.c:669
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:302
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:1012
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:1854
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:718
static bool is_reply(struct Email *reply, struct Email *orig)
Is one email a reply to another?
Definition send.c:1610
void mutt_encode_descriptions(struct Body *b, bool recurse, struct ConfigSubset *sub)
RFC2047 encode the content-descriptions.
Definition send.c:1501
void mutt_make_attribution_intro(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add "on DATE, PERSON wrote" header.
Definition send.c:669
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:1674
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:1090
int mutt_edit_address(struct AddressList *al, const char *field, bool expand_aliases)
Edit an email address.
Definition send.c:184
void mutt_forward_intro(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add the "start of forwarded message" text.
Definition send.c:461
void mutt_make_forward_subject(struct Envelope *env, struct Email *e, struct ConfigSubset *sub)
Create a subject for a forwarded email.
Definition send.c:992
static void make_reference_headers(struct EmailArray *ea, struct Envelope *env, struct ConfigSubset *sub)
Generate reference headers for an email.
Definition send.c:1058
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:767
void mutt_fix_reply_recipients(struct Envelope *env, struct ConfigSubset *sub)
Remove duplicate recipients.
Definition send.c:962
static char * nntp_get_header(const char *s)
Get the trimmed header.
Definition send.c:369
int mutt_resend_message(FILE *fp, struct Mailbox *m, struct Email *e_cur, struct ConfigSubset *sub)
Resend an email.
Definition send.c:1561
static int include_forward(struct Mailbox *m, struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Write out a forwarded message.
Definition send.c:513
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:1163
static void remove_user(struct AddressList *al, bool leave_only)
Remove any address which matches the current user.
Definition send.c:138
static void add_message_id(struct ListHead *head, struct Envelope *env)
Add the email's message ID to a list.
Definition send.c:949
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:157
int mutt_fetch_recips(struct Envelope *out, struct Envelope *in, SendFlags flags, struct ConfigSubset *sub)
Generate recpients for a reply email.
Definition send.c:879
static void mutt_make_greeting(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add greetings string.
Definition send.c:693
static int invoke_mta(struct Mailbox *m, struct Email *e, struct ConfigSubset *sub)
Send an email.
Definition send.c:1438
struct Address * mutt_default_from(struct ConfigSubset *sub)
Get a default 'from' Address.
Definition send.c:1405
static void process_user_recips(struct Envelope *env)
Process the user headers.
Definition send.c:379
bool mutt_send_list_unsubscribe(struct Mailbox *m, struct Email *e)
Send a mailing-list unsubscription email.
Definition send.c:2987
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:646
static void process_user_header(struct Envelope *env)
Process the user headers.
Definition send.c:407
static int edit_envelope(struct Envelope *en, SendFlags flags, struct ConfigSubset *sub)
Edit Envelope fields.
Definition send.c:230
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:2907
static bool abort_for_missing_attachments(const struct Body *b, struct ConfigSubset *sub)
Should we abort sending because of missing attachments?
Definition send.c:1980
static int default_to(struct AddressList *to, struct Envelope *env, SendFlags flags, int hmfupto, struct ConfigSubset *sub)
Generate default email addresses.
Definition send.c:793
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:1352
static void fix_end_of_file(const char *data)
Ensure a file ends with a linefeed.
Definition send.c:1536
static bool search_attach_keyword(char *filename, struct ConfigSubset *sub)
Search an email for 'attachment' keywords.
Definition send.c:1629
void mutt_forward_trailer(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add a "end of forwarded message" text.
Definition send.c:485
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:2032
static bool is_text_plain(const struct Body *b)
Is a Body a text/plain MIME part?
Definition send.c:1969
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:572
bool mutt_send_list_subscribe(struct Mailbox *m, struct Email *e)
Send a mailing-list subscription email.
Definition send.c:2958
static void add_references(struct ListHead *head, struct Envelope *env)
Add the email's references to a list.
Definition send.c:938
static void decode_descriptions(struct Body *b)
RFC2047 decode them in case of an error.
Definition send.c:1519
void mutt_make_attribution_trailer(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add suffix to replied email text.
Definition send.c:680
void mutt_set_followup_to(struct Envelope *env, struct ConfigSubset *sub)
Set followup-to field.
Definition send.c:1281
static void append_signature(FILE *fp, struct ConfigSubset *sub)
Append a signature to an email.
Definition send.c:101
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:1040
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:713
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:971
void mutt_unprepare_envelope(struct Envelope *env)
Undo the encodings of mutt_prepare_envelope()
Definition sendlib.c:785
void mutt_prepare_envelope(struct Envelope *env, bool final, struct ConfigSubset *sub)
Prepare an email header.
Definition sendlib.c:746
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:1024
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:299
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:1128
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
Autocrypt private Module data.
Definition module_data.h:32
char * autocrypt_default_key
Autocrypt default key id (used for postponing messages)
Definition module_data.h:35
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:57
char * username
User's login name.
Definition neomutt.h:56
char * home_dir
User's home directory.
Definition neomutt.h:55
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:33
struct ListHead user_header
Custom headers to add to outgoing emails.
Definition module_data.h:35
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_draft(buf)
Definition tmp.h:34
#define buf_mktemp(buf)
Definition tmp.h:33