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