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