NeoMutt  2022-04-29-215-gc12b98
Teaching an old dog new tricks
DOXYGEN
send.c
Go to the documentation of this file.
1
30#include "config.h"
31#include <errno.h>
32#include <limits.h>
33#include <locale.h>
34#include <stdbool.h>
35#include <stdio.h>
36#include <string.h>
37#include <sys/stat.h>
38#include <unistd.h>
39#include "mutt/lib.h"
40#include "address/lib.h"
41#include "config/lib.h"
42#include "email/lib.h"
43#include "core/lib.h"
44#include "alias/lib.h"
45#include "gui/lib.h"
46#include "mutt.h"
47#include "send.h"
48#include "lib.h"
49#include "attach/lib.h"
50#include "browser/lib.h"
51#include "compose/lib.h"
52#include "enter/lib.h"
53#include "ncrypt/lib.h"
54#include "pager/lib.h"
55#include "pattern/lib.h"
56#include "postpone/lib.h"
57#include "question/lib.h"
58#include "copy.h"
59#include "format_flags.h"
60#include "handler.h"
61#include "hdrline.h"
62#include "hook.h"
63#include "maillist.h"
64#include "mutt_body.h"
65#include "mutt_globals.h"
66#include "mutt_header.h"
67#include "mutt_logging.h"
68#include "muttlib.h"
69#include "options.h"
70#include "protos.h"
71#include "rfc3676.h"
72#include "sort.h"
73#ifdef USE_NNTP
74#include "mx.h"
75#include "nntp/mdata.h"
76#endif
77#ifdef MIXMASTER
78#include "mixmaster/lib.h"
79#endif
80#ifdef USE_NOTMUCH
81#include "notmuch/lib.h"
82#endif
83#ifdef USE_IMAP
84#include "imap/lib.h"
85#endif
86#ifdef USE_AUTOCRYPT
87#include "autocrypt/lib.h"
88#endif
89
95static void append_signature(FILE *fp, struct ConfigSubset *sub)
96{
97 const char *const c_signature = cs_subset_path(sub, "signature");
98 if (!c_signature)
99 return;
100
101 // If the user hasn't set $signature, don't warn them if it doesn't exist
102 struct Buffer *def_sig = mutt_buffer_pool_get();
103 cs_str_initial_get(sub->cs, "signature", def_sig);
104 mutt_path_canon(def_sig->data, def_sig->dsize, HomeDir, false);
105 bool notify_missing = !mutt_str_equal(c_signature, mutt_buffer_string(def_sig));
106 mutt_buffer_pool_release(&def_sig);
107
108 pid_t pid = 0;
109 FILE *fp_tmp = mutt_open_read(c_signature, &pid);
110 if (!fp_tmp)
111 {
112 if (notify_missing)
113 mutt_perror(c_signature);
114 return;
115 }
116
117 const bool c_sig_dashes = cs_subset_bool(sub, "sig_dashes");
118 if (c_sig_dashes)
119 fputs("\n-- \n", fp);
120 mutt_file_copy_stream(fp_tmp, fp);
121 mutt_file_fclose(&fp_tmp);
122 if (pid != -1)
123 filter_wait(pid);
124}
125
132static void remove_user(struct AddressList *al, bool leave_only)
133{
134 struct Address *a = NULL, *tmp = NULL;
135 TAILQ_FOREACH_SAFE(a, al, entries, tmp)
136 {
137 if (mutt_addr_is_user(a) && (!leave_only || TAILQ_NEXT(a, entries)))
138 {
139 TAILQ_REMOVE(al, a, entries);
140 mutt_addr_free(&a);
141 }
142 }
143}
144
151static void add_mailing_lists(struct AddressList *out, const struct AddressList *t,
152 const struct AddressList *c)
153{
154 const struct AddressList *const als[] = { t, c };
155
156 for (size_t i = 0; i < mutt_array_size(als); ++i)
157 {
158 const struct AddressList *al = als[i];
159 struct Address *a = NULL;
160 TAILQ_FOREACH(a, al, entries)
161 {
162 if (!a->group && mutt_is_mail_list(a))
163 {
165 }
166 }
167 }
168}
169
178int mutt_edit_address(struct AddressList *al, const char *field, bool expand_aliases)
179{
180 int rc = 0;
181 struct Buffer *buf = mutt_buffer_pool_get();
182 mutt_buffer_alloc(buf, 8192);
183 char *err = NULL;
184 int idna_ok = 0;
185
186 do
187 {
190 mutt_addrlist_write(al, buf->data, buf->dsize, false);
191 if (mutt_buffer_get_field(field, buf, MUTT_COMP_ALIAS, false, NULL, NULL, NULL) != 0)
192 {
193 rc = -1;
194 goto done;
195 }
198 if (expand_aliases)
200 idna_ok = mutt_addrlist_to_intl(al, &err);
201 if (idna_ok != 0)
202 {
203 mutt_error(_("Bad IDN: '%s'"), err);
204 FREE(&err);
205 }
206 } while (idna_ok != 0);
207
208done:
210 return rc;
211}
212
221static int edit_envelope(struct Envelope *en, SendFlags flags, struct ConfigSubset *sub)
222{
223 int rc = -1;
224 struct Buffer *buf = mutt_buffer_pool_get();
225 mutt_buffer_alloc(buf, 8192);
226
227#ifdef USE_NNTP
228 if (OptNewsSend)
229 {
230 if (en->newsgroups)
232 else
234
235 if (mutt_buffer_get_field("Newsgroups: ", buf, MUTT_COMP_NO_FLAGS, false,
236 NULL, NULL, NULL) != 0)
237 {
238 goto done;
239 }
241
242 if (en->followup_to)
244 else
246
247 const bool c_ask_follow_up = cs_subset_bool(sub, "ask_follow_up");
248 if (c_ask_follow_up && (mutt_buffer_get_field("Followup-To: ", buf, MUTT_COMP_NO_FLAGS,
249 false, NULL, NULL, NULL) != 0))
250 {
251 goto done;
252 }
254
255 if (en->x_comment_to)
257 else
259
260 const bool c_x_comment_to = cs_subset_bool(sub, "x_comment_to");
261 const bool c_ask_x_comment_to = cs_subset_bool(sub, "ask_x_comment_to");
262 if (c_x_comment_to && c_ask_x_comment_to &&
263 (mutt_buffer_get_field("X-Comment-To: ", buf, MUTT_COMP_NO_FLAGS, false,
264 NULL, NULL, NULL) != 0))
265 {
266 goto done;
267 }
269 }
270 else
271#endif
272 {
273 const bool c_fast_reply = cs_subset_bool(sub, "fast_reply");
274 if (TAILQ_EMPTY(&en->to) || !c_fast_reply || (flags & SEND_REVIEW_TO))
275 {
276 if ((mutt_edit_address(&en->to, _("To: "), true) == -1))
277 goto done;
278 }
279
280 const bool c_ask_cc = cs_subset_bool(sub, "ask_cc");
281 if (TAILQ_EMPTY(&en->cc) || !c_fast_reply)
282 {
283 if (c_ask_cc && (mutt_edit_address(&en->cc, _("Cc: "), true) == -1))
284 goto done;
285 }
286
287 const bool c_ask_bcc = cs_subset_bool(sub, "ask_bcc");
288 if (TAILQ_EMPTY(&en->bcc) || !c_fast_reply)
289 {
290 if (c_ask_bcc && (mutt_edit_address(&en->bcc, _("Bcc: "), true) == -1))
291 goto done;
292 }
293
294 if (TAILQ_EMPTY(&en->to) && TAILQ_EMPTY(&en->cc) && TAILQ_EMPTY(&en->bcc))
295 {
296 mutt_warning(_("No recipients specified"));
297 goto done;
298 }
299
300 const bool c_reply_with_xorig = cs_subset_bool(sub, "reply_with_xorig");
301 if (c_reply_with_xorig && (flags & (SEND_REPLY | SEND_LIST_REPLY | SEND_GROUP_REPLY)) &&
302 (mutt_edit_address(&en->from, "From: ", true) == -1))
303 {
304 goto done;
305 }
306 }
307
308 if (en->subject)
309 {
310 const bool c_fast_reply = cs_subset_bool(sub, "fast_reply");
311 if (c_fast_reply)
312 {
313 rc = 0;
314 goto done;
315 }
316 mutt_buffer_strcpy(buf, en->subject);
317 }
318 else
319 {
320 const char *p = NULL;
321
323 struct ListNode *uh = NULL;
324 STAILQ_FOREACH(uh, &UserHeader, entries)
325 {
326 size_t plen = mutt_istr_startswith(uh->data, "subject:");
327 if (plen)
328 {
330 mutt_buffer_strcpy(buf, p);
331 }
332 }
333 }
334
335 const enum QuadOption c_abort_nosubject = cs_subset_quad(sub, "abort_nosubject");
336 if ((mutt_buffer_get_field(_("Subject: "), buf, MUTT_COMP_NO_FLAGS, false,
337 NULL, NULL, NULL) != 0) ||
338 (mutt_buffer_is_empty(buf) &&
339 (query_quadoption(c_abort_nosubject, _("No subject, abort?")) != MUTT_NO)))
340 {
341 mutt_message(_("No subject, aborting"));
342 goto done;
343 }
345 rc = 0;
346
347done:
349 return rc;
350}
351
352#ifdef USE_NNTP
360static char *nntp_get_header(const char *s)
361{
362 SKIPWS(s);
363 return mutt_str_dup(s);
364}
365#endif
366
371static void process_user_recips(struct Envelope *env)
372{
373 struct ListNode *uh = NULL;
374 STAILQ_FOREACH(uh, &UserHeader, entries)
375 {
376 size_t plen;
377 if ((plen = mutt_istr_startswith(uh->data, "to:")))
378 mutt_addrlist_parse(&env->to, uh->data + plen);
379 else if ((plen = mutt_istr_startswith(uh->data, "cc:")))
380 mutt_addrlist_parse(&env->cc, uh->data + plen);
381 else if ((plen = mutt_istr_startswith(uh->data, "bcc:")))
382 mutt_addrlist_parse(&env->bcc, uh->data + plen);
383#ifdef USE_NNTP
384 else if ((plen = mutt_istr_startswith(uh->data, "newsgroups:")))
385 env->newsgroups = nntp_get_header(uh->data + plen);
386 else if ((plen = mutt_istr_startswith(uh->data, "followup-to:")))
387 env->followup_to = nntp_get_header(uh->data + plen);
388 else if ((plen = mutt_istr_startswith(uh->data, "x-comment-to:")))
390#endif
391 }
392}
393
398static void process_user_header(struct Envelope *env)
399{
400 struct ListNode *uh = NULL;
401 STAILQ_FOREACH(uh, &UserHeader, entries)
402 {
403 size_t plen;
404 if ((plen = mutt_istr_startswith(uh->data, "from:")))
405 {
406 /* User has specified a default From: address. Remove default address */
408 mutt_addrlist_parse(&env->from, uh->data + plen);
409 }
410 else if ((plen = mutt_istr_startswith(uh->data, "reply-to:")))
411 {
414 }
415 else if ((plen = mutt_istr_startswith(uh->data, "message-id:")))
416 {
417 char *tmp = mutt_extract_message_id(uh->data + plen, NULL);
418 if (mutt_addr_valid_msgid(tmp))
419 {
420 FREE(&env->message_id);
421 env->message_id = tmp;
422 }
423 else
424 FREE(&tmp);
425 }
426 else if (!mutt_istr_startswith(uh->data, "to:") &&
427 !mutt_istr_startswith(uh->data, "cc:") &&
428 !mutt_istr_startswith(uh->data, "bcc:") &&
429#ifdef USE_NNTP
430 !mutt_istr_startswith(uh->data, "newsgroups:") &&
431 !mutt_istr_startswith(uh->data, "followup-to:") &&
432 !mutt_istr_startswith(uh->data, "x-comment-to:") &&
433#endif
434 !mutt_istr_startswith(uh->data, "supersedes:") &&
435 !mutt_istr_startswith(uh->data, "subject:") &&
436 !mutt_istr_startswith(uh->data, "return-path:"))
437 {
439 }
440 }
441}
442
449void mutt_forward_intro(struct Email *e, FILE *fp, struct ConfigSubset *sub)
450{
451 const char *const c_forward_attribution_intro = cs_subset_string(sub, "forward_attribution_intro");
452 if (!c_forward_attribution_intro || !fp)
453 return;
454
455 const char *const c_attribution_locale = cs_subset_string(sub, "attribution_locale");
456
457 char buf[1024] = { 0 };
458 setlocale(LC_TIME, NONULL(c_attribution_locale));
459 mutt_make_string(buf, sizeof(buf), 0, c_forward_attribution_intro, NULL, -1,
460 e, MUTT_FORMAT_NO_FLAGS, NULL);
461 setlocale(LC_TIME, "");
462 fputs(buf, fp);
463 fputs("\n\n", fp);
464}
465
472void mutt_forward_trailer(struct Email *e, FILE *fp, struct ConfigSubset *sub)
473{
474 const char *const c_forward_attribution_trailer = cs_subset_string(sub, "forward_attribution_trailer");
475 if (!c_forward_attribution_trailer || !fp)
476 return;
477
478 const char *const c_attribution_locale = cs_subset_string(sub, "attribution_locale");
479
480 char buf[1024] = { 0 };
481 setlocale(LC_TIME, NONULL(c_attribution_locale));
482 mutt_make_string(buf, sizeof(buf), 0, c_forward_attribution_trailer, NULL, -1,
483 e, MUTT_FORMAT_NO_FLAGS, NULL);
484 setlocale(LC_TIME, "");
485 fputc('\n', fp);
486 fputs(buf, fp);
487 fputc('\n', fp);
488}
489
499static int include_forward(struct Mailbox *m, struct Email *e, FILE *fp_out,
500 struct ConfigSubset *sub)
501{
502 CopyHeaderFlags chflags = CH_DECODE;
504
505 struct Message *msg = mx_msg_open(m, e->msgno);
506 if (!msg)
507 {
508 return -1;
509 }
512
513 const bool c_forward_decode = cs_subset_bool(sub, "forward_decode");
514 if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT) && c_forward_decode)
515 {
516 /* make sure we have the user's passphrase before proceeding... */
518 {
519 mx_msg_close(m, &msg);
520 return -1;
521 }
522 }
523
524 mutt_forward_intro(e, fp_out, sub);
525
526 if (c_forward_decode)
527 {
528 cmflags |= MUTT_CM_DECODE | MUTT_CM_CHARCONV;
529
530 const bool c_weed = cs_subset_bool(sub, "weed");
531 if (c_weed)
532 {
533 chflags |= CH_WEED | CH_REORDER;
534 cmflags |= MUTT_CM_WEED;
535 }
536 }
537
538 const bool c_forward_quote = cs_subset_bool(sub, "forward_quote");
539 if (c_forward_quote)
540 cmflags |= MUTT_CM_PREFIX;
541
542 mutt_copy_message(fp_out, e, msg, cmflags, chflags, 0);
543 mx_msg_close(m, &msg);
544 mutt_forward_trailer(e, fp_out, sub);
545 return 0;
546}
547
558static int inline_forward_attachments(struct Mailbox *m, struct Email *e,
559 struct Body ***plast, enum QuadOption *forwardq,
560 struct ConfigSubset *sub)
561{
562 struct Body **last = *plast;
563 struct Body *body = NULL;
564 struct AttachCtx *actx = NULL;
565 int rc = 0, i;
566
567 struct Message *msg = mx_msg_open(m, e->msgno);
568 if (!msg)
569 {
570 return -1;
571 }
572
575
576 actx = mutt_mem_calloc(1, sizeof(*actx));
577 actx->email = e;
578 actx->fp_root = msg->fp;
579
580 mutt_generate_recvattach_list(actx, actx->email, actx->email->body,
581 actx->fp_root, -1, 0, 0);
582
583 for (i = 0; i < actx->idxlen; i++)
584 {
585 body = actx->idx[i]->body;
586 if ((body->type != TYPE_MULTIPART) && mutt_prefer_as_attachment(body) &&
587 !((body->type == TYPE_APPLICATION) &&
588 (mutt_istr_equal(body->subtype, "pgp-signature") ||
589 mutt_istr_equal(body->subtype, "x-pkcs7-signature") ||
590 mutt_istr_equal(body->subtype, "pkcs7-signature"))))
591 {
592 /* Ask the quadoption only once */
593 if (*forwardq == MUTT_ABORT)
594 {
595 const enum QuadOption c_forward_attachments = cs_subset_quad(sub, "forward_attachments");
596 *forwardq = query_quadoption(c_forward_attachments,
597 /* L10N: This is the prompt for $forward_attachments.
598 When inline forwarding ($mime_forward answered "no"), this prompts
599 whether to add non-decodable attachments from the original email.
600 Text/plain parts and the like will already be included in the
601 message contents, but other attachment, such as PDF files, will also
602 be added as attachments to the new mail, if this is answered yes. */
603 _("Forward attachments?"));
604 if (*forwardq != MUTT_YES)
605 {
606 if (*forwardq == -1)
607 rc = -1;
608 goto cleanup;
609 }
610 }
611 if (mutt_body_copy(actx->idx[i]->fp, last, body) == -1)
612 {
613 rc = -1;
614 goto cleanup;
615 }
616 last = &((*last)->next);
617 }
618 }
619
620cleanup:
621 *plast = last;
622 mx_msg_close(m, &msg);
623 mutt_actx_free(&actx);
624 return rc;
625}
626
633void mutt_make_attribution(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
634{
635 const char *const c_attribution = cs_subset_string(sub, "attribution");
636 if (!c_attribution || !fp_out)
637 return;
638
639 const char *const c_attribution_locale = cs_subset_string(sub, "attribution_locale");
640
641 char buf[1024] = { 0 };
642 setlocale(LC_TIME, NONULL(c_attribution_locale));
643 mutt_make_string(buf, sizeof(buf), 0, c_attribution, NULL, -1, e,
645 setlocale(LC_TIME, "");
646 fputs(buf, fp_out);
647 fputc('\n', fp_out);
648}
649
659static const char *greeting_format_str(char *buf, size_t buflen, size_t col, int cols,
660 char op, const char *src, const char *prec,
661 const char *if_str, const char *else_str,
662 intptr_t data, MuttFormatFlags flags)
663{
664 struct Email *e = (struct Email *) data;
665 char *p = NULL;
666 char buf2[256];
667
668 const struct Address *to = TAILQ_FIRST(&e->env->to);
669 const struct Address *cc = TAILQ_FIRST(&e->env->cc);
670
671 buf[0] = '\0';
672 switch (op)
673 {
674 case 'n':
675 mutt_format_s(buf, buflen, prec, mutt_get_name(to));
676 break;
677
678 case 'u':
679 if (to)
680 {
681 mutt_str_copy(buf2, mutt_addr_for_display(to), sizeof(buf2));
682 if ((p = strpbrk(buf2, "%@")))
683 *p = '\0';
684 }
685 else
686 buf2[0] = '\0';
687 mutt_format_s(buf, buflen, prec, buf2);
688 break;
689
690 case 'v':
691 if (to)
692 mutt_format_s(buf2, sizeof(buf2), prec, mutt_get_name(to));
693 else if (cc)
694 mutt_format_s(buf2, sizeof(buf2), prec, mutt_get_name(cc));
695 else
696 *buf2 = '\0';
697 if ((p = strpbrk(buf2, " %@")))
698 *p = '\0';
699 mutt_format_s(buf, buflen, prec, buf2);
700 break;
701
702 default:
703 snprintf(buf, buflen, "%%%s%c", prec, op);
704 break;
705 }
706
707 if (flags & MUTT_FORMAT_OPTIONAL)
708 mutt_expando_format(buf, buflen, col, cols, else_str, greeting_format_str, data, flags);
709
710 return src;
711}
712
721static void mutt_make_greeting(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
722{
723 const char *const c_greeting = cs_subset_string(sub, "greeting");
724 if (!c_greeting || !fp_out)
725 return;
726
727 char buf[1024] = { 0 };
728
729 mutt_expando_format(buf, sizeof(buf), 0, 0, c_greeting, greeting_format_str,
730 (intptr_t) e, MUTT_TOKEN_NO_FLAGS);
731
732 fputs(buf, fp_out);
733 fputc('\n', fp_out);
734}
735
742void mutt_make_post_indent(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
743{
744 const char *const c_post_indent_string = cs_subset_string(sub, "post_indent_string");
745 if (!c_post_indent_string || !fp_out)
746 return;
747
748 char buf[256] = { 0 };
749 mutt_make_string(buf, sizeof(buf), 0, c_post_indent_string, NULL, -1, e,
751 fputs(buf, fp_out);
752 fputc('\n', fp_out);
753}
754
764static int include_reply(struct Mailbox *m, struct Email *e, FILE *fp_out,
765 struct ConfigSubset *sub)
766{
768 CopyHeaderFlags chflags = CH_DECODE;
769
770 if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT))
771 {
772 /* make sure we have the user's passphrase before proceeding... */
774 return -1;
775 }
776
777 struct Message *msg = mx_msg_open(m, e->msgno);
778 if (!msg)
779 {
780 return -1;
781 }
784
785 mutt_make_attribution(e, fp_out, sub);
786
787 const bool c_header = cs_subset_bool(sub, "header");
788 if (!c_header)
789 cmflags |= MUTT_CM_NOHEADER;
790
791 const bool c_weed = cs_subset_bool(sub, "weed");
792 if (c_weed)
793 {
794 chflags |= CH_WEED | CH_REORDER;
795 cmflags |= MUTT_CM_WEED;
796 }
797
798 mutt_copy_message(fp_out, e, msg, cmflags, chflags, 0);
799 mx_msg_close(m, &msg);
800
801 mutt_make_post_indent(e, fp_out, sub);
802
803 return 0;
804}
805
813static const struct AddressList *choose_default_to(const struct Address *from,
814 const struct Envelope *env,
815 struct ConfigSubset *sub)
816{
817 const bool c_reply_self = cs_subset_bool(sub, "reply_self");
818 if (!c_reply_self && mutt_addr_is_user(from))
819 {
820 /* mail is from the user, assume replying to recipients */
821 return &env->to;
822 }
823 else
824 {
825 return &env->from;
826 }
827}
828
839static int default_to(struct AddressList *to, struct Envelope *env,
840 SendFlags flags, int hmfupto, struct ConfigSubset *sub)
841{
842 const struct Address *from = TAILQ_FIRST(&env->from);
843 const struct Address *reply_to = TAILQ_FIRST(&env->reply_to);
844
845 if (flags && !TAILQ_EMPTY(&env->mail_followup_to) && (hmfupto == MUTT_YES))
846 {
847 mutt_addrlist_copy(to, &env->mail_followup_to, true);
848 return 0;
849 }
850
851 /* Exit now if we're setting up the default Cc list for list-reply
852 * (only set if Mail-Followup-To is present and honoured). */
853 if (flags & SEND_LIST_REPLY)
854 return 0;
855
856 const struct AddressList *default_to = choose_default_to(from, env, sub);
857
858 if (reply_to)
859 {
860 const bool from_is_reply_to = mutt_addr_cmp(from, reply_to);
861 const bool multiple_reply_to = reply_to &&
862 TAILQ_NEXT(TAILQ_FIRST(&env->reply_to), entries);
863
864 const bool c_ignore_list_reply_to = cs_subset_bool(sub, "ignore_list_reply_to");
865 const enum QuadOption c_reply_to = cs_subset_quad(sub, "reply_to");
866 if ((from_is_reply_to && !multiple_reply_to && !reply_to->personal) ||
867 (c_ignore_list_reply_to && mutt_is_mail_list(reply_to) &&
868 (mutt_addrlist_search(&env->to, reply_to) || mutt_addrlist_search(&env->cc, reply_to))))
869 {
870 /* If the Reply-To: address is a mailing list, assume that it was
871 * put there by the mailing list, and use the From: address
872 *
873 * We also take the from header if our correspondent has a reply-to
874 * header which is identical to the electronic mail address given
875 * in his From header, and the reply-to has no display-name. */
876 mutt_addrlist_copy(to, &env->from, false);
877 }
878 else if (!(from_is_reply_to && !multiple_reply_to) && (c_reply_to != MUTT_YES))
879 {
880 char prompt[256] = { 0 };
881 /* There are quite a few mailing lists which set the Reply-To:
882 * header field to the list address, which makes it quite impossible
883 * to send a message to only the sender of the message. This
884 * provides a way to do that. */
885 /* L10N: Asks whether the user respects the reply-to header.
886 If she says no, neomutt will reply to the from header's address instead. */
887 snprintf(prompt, sizeof(prompt), _("Reply to %s%s?"), reply_to->mailbox,
888 multiple_reply_to ? ",..." : "");
889 switch (query_quadoption(c_reply_to, prompt))
890 {
891 case MUTT_YES:
892 mutt_addrlist_copy(to, &env->reply_to, false);
893 break;
894
895 case MUTT_NO:
896 mutt_addrlist_copy(to, default_to, false);
897 break;
898
899 default:
900 return -1; /* abort */
901 }
902 }
903 else
904 {
905 mutt_addrlist_copy(to, &env->reply_to, false);
906 }
907 }
908 else
909 {
910 mutt_addrlist_copy(to, default_to, false);
911 }
912
913 return 0;
914}
915
925int mutt_fetch_recips(struct Envelope *out, struct Envelope *in,
926 SendFlags flags, struct ConfigSubset *sub)
927{
928 enum QuadOption hmfupto = MUTT_ABORT;
929 const struct Address *followup_to = TAILQ_FIRST(&in->mail_followup_to);
930
931 if ((flags & (SEND_LIST_REPLY | SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY)) && followup_to)
932 {
933 char prompt[256] = { 0 };
934 snprintf(prompt, sizeof(prompt), _("Follow-up to %s%s?"), followup_to->mailbox,
935 TAILQ_NEXT(TAILQ_FIRST(&in->mail_followup_to), entries) ? ",..." : "");
936
937 const enum QuadOption c_honor_followup_to = cs_subset_quad(sub, "honor_followup_to");
938 hmfupto = query_quadoption(c_honor_followup_to, prompt);
939 if (hmfupto == MUTT_ABORT)
940 return -1;
941 }
942
943 if (flags & SEND_LIST_REPLY)
944 {
945 add_mailing_lists(&out->to, &in->to, &in->cc);
946
947 if (followup_to && (hmfupto == MUTT_YES) &&
948 (default_to(&out->cc, in, flags & SEND_LIST_REPLY, (hmfupto == MUTT_YES), sub) == MUTT_ABORT))
949 {
950 return -1; /* abort */
951 }
952 }
953 else if (flags & SEND_TO_SENDER)
954 {
955 mutt_addrlist_copy(&out->to, &in->from, false);
956 }
957 else
958 {
959 if (default_to(&out->to, in, flags & (SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY),
960 (hmfupto == MUTT_YES), sub) == -1)
961 {
962 return -1; /* abort */
963 }
964
965 if ((flags & (SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY)) &&
966 (!followup_to || (hmfupto != MUTT_YES)))
967 {
968 /* if(!mutt_addr_is_user(in->to)) */
969 if (flags & SEND_GROUP_REPLY)
970 mutt_addrlist_copy(&out->cc, &in->to, true);
971 else
972 mutt_addrlist_copy(&out->to, &in->to, true);
973 mutt_addrlist_copy(&out->cc, &in->cc, true);
974 }
975 }
976 return 0;
977}
978
984static void add_references(struct ListHead *head, struct Envelope *env)
985{
986 struct ListNode *np = NULL;
987
988 struct ListHead *src = STAILQ_EMPTY(&env->references) ? &env->in_reply_to : &env->references;
989 STAILQ_FOREACH(np, src, entries)
990 {
992 }
993}
994
1000static void add_message_id(struct ListHead *head, struct Envelope *env)
1001{
1002 if (env->message_id)
1003 {
1005 }
1006}
1007
1014{
1015 const bool c_me_too = cs_subset_bool(sub, "me_too");
1016 if (!c_me_too)
1017 {
1018 const bool c_reply_self = cs_subset_bool(sub, "reply_self");
1019
1020 /* the order is important here. do the CC: first so that if the
1021 * the user is the only recipient, it ends up on the TO: field */
1022 remove_user(&env->cc, TAILQ_EMPTY(&env->to));
1023 remove_user(&env->to, TAILQ_EMPTY(&env->cc) || c_reply_self);
1024 }
1025
1026 /* the CC field can get cluttered, especially with lists */
1027 mutt_addrlist_dedupe(&env->to);
1028 mutt_addrlist_dedupe(&env->cc);
1029 mutt_addrlist_remove_xrefs(&env->to, &env->cc);
1030
1031 if (!TAILQ_EMPTY(&env->cc) && TAILQ_EMPTY(&env->to))
1032 {
1033 TAILQ_SWAP(&env->to, &env->cc, Address, entries);
1034 }
1035}
1036
1043void mutt_make_forward_subject(struct Envelope *env, struct Email *e, struct ConfigSubset *sub)
1044{
1045 if (!env)
1046 return;
1047
1048 const char *const c_forward_format = cs_subset_string(sub, "forward_format");
1049
1050 char buf[256] = { 0 };
1051 /* set the default subject for the message. */
1052 mutt_make_string(buf, sizeof(buf), 0, NONULL(c_forward_format), NULL, -1, e,
1053 MUTT_FORMAT_NO_FLAGS, NULL);
1054 mutt_str_replace(&env->subject, buf);
1055}
1056
1063void mutt_make_misc_reply_headers(struct Envelope *env, struct Envelope *curenv,
1064 struct ConfigSubset *sub)
1065{
1066 if (!env || !curenv)
1067 return;
1068
1069 /* This takes precedence over a subject that might have
1070 * been taken from a List-Post header. Is that correct? */
1071 if (curenv->real_subj)
1072 {
1073 FREE(&env->subject);
1074 env->subject = mutt_mem_malloc(mutt_str_len(curenv->real_subj) + 5);
1075 sprintf(env->subject, "Re: %s", curenv->real_subj);
1076 }
1077 else if (!env->subject)
1078 {
1079 const char *const c_empty_subject = cs_subset_string(sub, "empty_subject");
1080 env->subject = mutt_str_dup(c_empty_subject);
1081 }
1082}
1083
1090void mutt_add_to_reference_headers(struct Envelope *env, struct Envelope *curenv,
1091 struct ConfigSubset *sub)
1092{
1093 add_references(&env->references, curenv);
1094 add_message_id(&env->references, curenv);
1095 add_message_id(&env->in_reply_to, curenv);
1096
1097#ifdef USE_NNTP
1098 const bool c_x_comment_to = cs_subset_bool(sub, "x_comment_to");
1099 if (OptNewsSend && c_x_comment_to && !TAILQ_EMPTY(&curenv->from))
1101#endif
1102}
1103
1110static void make_reference_headers(struct EmailList *el, struct Envelope *env,
1111 struct ConfigSubset *sub)
1112{
1113 if (!el || !env || STAILQ_EMPTY(el))
1114 return;
1115
1116 struct EmailNode *en = STAILQ_FIRST(el);
1117 bool single = !STAILQ_NEXT(en, entries);
1118
1119 if (!single)
1120 {
1121 STAILQ_FOREACH(en, el, entries)
1122 {
1123 mutt_add_to_reference_headers(env, en->email->env, sub);
1124 }
1125 }
1126 else
1127 mutt_add_to_reference_headers(env, en->email->env, sub);
1128
1129 /* if there's more than entry in In-Reply-To (i.e. message has multiple
1130 * parents), don't generate a References: header as it's discouraged by
1131 * RFC2822, sect. 3.6.4 */
1132 if (!single && !STAILQ_EMPTY(&env->in_reply_to) &&
1133 STAILQ_NEXT(STAILQ_FIRST(&env->in_reply_to), entries))
1134 {
1136 }
1137}
1138
1148static int envelope_defaults(struct Envelope *env, struct EmailList *el,
1149 SendFlags flags, struct ConfigSubset *sub)
1150{
1151 if (!el || STAILQ_EMPTY(el))
1152 return -1;
1153
1154 struct EmailNode *en = STAILQ_FIRST(el);
1155 bool single = !STAILQ_NEXT(en, entries);
1156
1157 struct Envelope *curenv = en->email->env;
1158 if (!curenv)
1159 return -1;
1160
1161 if (flags & (SEND_REPLY | SEND_TO_SENDER))
1162 {
1163#ifdef USE_NNTP
1164 if ((flags & SEND_NEWS))
1165 {
1166 /* in case followup set Newsgroups: with Followup-To: if it present */
1167 if (!env->newsgroups && !mutt_istr_equal(curenv->followup_to, "poster"))
1168 {
1169 env->newsgroups = mutt_str_dup(curenv->followup_to);
1170 }
1171 }
1172 else
1173#endif
1174 if (!single)
1175 {
1176 STAILQ_FOREACH(en, el, entries)
1177 {
1178 if (mutt_fetch_recips(env, en->email->env, flags, sub) == -1)
1179 return -1;
1180 }
1181 }
1182 else if (mutt_fetch_recips(env, curenv, flags, sub) == -1)
1183 return -1;
1184
1185 if ((flags & SEND_LIST_REPLY) && TAILQ_EMPTY(&env->to))
1186 {
1187 mutt_error(_("No mailing lists found"));
1188 return -1;
1189 }
1190
1191 if (flags & SEND_REPLY)
1192 {
1193 mutt_make_misc_reply_headers(env, curenv, sub);
1194 make_reference_headers(el, env, sub);
1195 }
1196 }
1197 else if (flags & SEND_FORWARD)
1198 {
1199 mutt_make_forward_subject(env, en->email, sub);
1200
1201 const bool c_forward_references = cs_subset_bool(sub, "forward_references");
1202 if (c_forward_references)
1203 make_reference_headers(el, env, sub);
1204 }
1205
1206 return 0;
1207}
1208
1220static int generate_body(FILE *fp_tmp, struct Email *e, SendFlags flags,
1221 struct Mailbox *m, struct EmailList *el, struct ConfigSubset *sub)
1222{
1223 /* An EmailList is required for replying and forwarding */
1224 if (!el && (flags & (SEND_REPLY | SEND_FORWARD)))
1225 return -1;
1226
1227 if (flags & SEND_REPLY)
1228 {
1229 const enum QuadOption c_include = cs_subset_quad(sub, "include");
1230 enum QuadOption ans = query_quadoption(c_include, _("Include message in reply?"));
1231 if (ans == MUTT_ABORT)
1232 return -1;
1233
1234 if (ans == MUTT_YES)
1235 {
1236 mutt_message(_("Including quoted message..."));
1237 struct EmailNode *en = NULL;
1238 STAILQ_FOREACH(en, el, entries)
1239 {
1240 if (include_reply(m, en->email, fp_tmp, sub) == -1)
1241 {
1242 mutt_error(_("Could not include all requested messages"));
1243 return -1;
1244 }
1245 if (STAILQ_NEXT(en, entries) != NULL)
1246 {
1247 fputc('\n', fp_tmp);
1248 }
1249 }
1250 }
1251 }
1252 else if (flags & SEND_FORWARD)
1253 {
1254 const enum QuadOption c_mime_forward = cs_subset_quad(sub, "mime_forward");
1255 enum QuadOption ans = query_quadoption(c_mime_forward, _("Forward as attachment?"));
1256 if (ans == MUTT_YES)
1257 {
1258 struct Body *last = e->body;
1259
1260 mutt_message(_("Preparing forwarded message..."));
1261
1262 while (last && last->next)
1263 last = last->next;
1264
1265 struct EmailNode *en = NULL;
1266 STAILQ_FOREACH(en, el, entries)
1267 {
1268 struct Body *tmp = mutt_make_message_attach(m, en->email, false, sub);
1269 if (last)
1270 {
1271 last->next = tmp;
1272 last = tmp;
1273 }
1274 else
1275 {
1276 last = tmp;
1277 e->body = tmp;
1278 }
1279 }
1280 }
1281 else if (ans != MUTT_ABORT)
1282 {
1283 enum QuadOption forwardq = MUTT_ABORT;
1284 struct Body **last = NULL;
1285 struct EmailNode *en = NULL;
1286
1287 const bool c_forward_decode = cs_subset_bool(sub, "forward_decode");
1288 const enum QuadOption c_forward_attachments = cs_subset_quad(sub, "forward_attachments");
1289 if (c_forward_decode && (c_forward_attachments != MUTT_NO))
1290 {
1291 last = &e->body;
1292 while (*last)
1293 last = &((*last)->next);
1294 }
1295
1296 STAILQ_FOREACH(en, el, entries)
1297 {
1298 struct Email *e_cur = en->email;
1299 include_forward(m, e_cur, fp_tmp, sub);
1300 if (c_forward_decode && (c_forward_attachments != MUTT_NO))
1301 {
1302 if (inline_forward_attachments(m, e_cur, &last, &forwardq, sub) != 0)
1303 return -1;
1304 }
1305 }
1306 }
1307 else
1308 return -1;
1309 }
1310 /* if (WithCrypto && (flags & SEND_KEY)) */
1311 else if (((WithCrypto & APPLICATION_PGP) != 0) && (flags & SEND_KEY))
1312 {
1313 struct Body *b = NULL;
1314
1315 if (((WithCrypto & APPLICATION_PGP) != 0) && !(b = crypt_pgp_make_key_attachment()))
1316 {
1317 return -1;
1318 }
1319
1320 b->next = e->body;
1321 e->body = b;
1322 }
1323
1325
1326 return 0;
1327}
1328
1334void mutt_set_followup_to(struct Envelope *env, struct ConfigSubset *sub)
1335{
1336 /* Only generate the Mail-Followup-To if the user has requested it, and
1337 * it hasn't already been set */
1338
1339 const bool c_followup_to = cs_subset_bool(sub, "followup_to");
1340 if (!c_followup_to)
1341 return;
1342#ifdef USE_NNTP
1343 if (OptNewsSend)
1344 {
1345 if (!env->followup_to && env->newsgroups && (strrchr(env->newsgroups, ',')))
1346 env->followup_to = mutt_str_dup(env->newsgroups);
1347 return;
1348 }
1349#endif
1350
1351 if (TAILQ_EMPTY(&env->mail_followup_to))
1352 {
1353 if (mutt_is_list_recipient(false, env))
1354 {
1355 /* this message goes to known mailing lists, so create a proper
1356 * mail-followup-to header */
1357
1358 mutt_addrlist_copy(&env->mail_followup_to, &env->to, false);
1359 mutt_addrlist_copy(&env->mail_followup_to, &env->cc, true);
1360 }
1361
1362 /* remove ourselves from the mail-followup-to header */
1363 remove_user(&env->mail_followup_to, false);
1364
1365 /* If we are not subscribed to any of the lists in question, re-add
1366 * ourselves to the mail-followup-to header. The mail-followup-to header
1367 * generated is a no-op with group-reply, but makes sure list-reply has the
1368 * desired effect. */
1369
1370 if (!TAILQ_EMPTY(&env->mail_followup_to) &&
1372 {
1373 struct AddressList *al = NULL;
1374 if (!TAILQ_EMPTY(&env->reply_to))
1375 al = &env->reply_to;
1376 else if (!TAILQ_EMPTY(&env->from))
1377 al = &env->from;
1378
1379 if (al)
1380 {
1381 struct Address *a = NULL;
1382 TAILQ_FOREACH_REVERSE(a, al, AddressList, entries)
1383 {
1385 }
1386 }
1387 else
1388 {
1390 }
1391 }
1392
1394 }
1395}
1396
1407static void set_reverse_name(struct AddressList *al, struct Envelope *env,
1408 struct ConfigSubset *sub)
1409{
1410 struct Address *a = NULL;
1411 if (TAILQ_EMPTY(al))
1412 {
1413 TAILQ_FOREACH(a, &env->to, entries)
1414 {
1415 if (mutt_addr_is_user(a))
1416 {
1418 break;
1419 }
1420 }
1421 }
1422
1423 if (TAILQ_EMPTY(al))
1424 {
1425 TAILQ_FOREACH(a, &env->cc, entries)
1426 {
1427 if (mutt_addr_is_user(a))
1428 {
1430 break;
1431 }
1432 }
1433 }
1434
1435 if (TAILQ_EMPTY(al))
1436 {
1437 struct Address *from = TAILQ_FIRST(&env->from);
1438 if (from && mutt_addr_is_user(from))
1439 {
1441 }
1442 }
1443
1444 if (!TAILQ_EMPTY(al))
1445 {
1446 /* when $reverse_real_name is not set, clear the personal name so that it
1447 * may be set via a reply- or send-hook. */
1448
1449 const bool c_reverse_real_name = cs_subset_bool(sub, "reverse_real_name");
1450 if (!c_reverse_real_name)
1451 FREE(&TAILQ_FIRST(al)->personal);
1452 }
1453}
1454
1460{
1461 /* Note: We let $from override $real_name here.
1462 * Is this the right thing to do?
1463 */
1464
1465 const struct Address *c_from = cs_subset_address(sub, "from");
1466 const bool c_use_domain = cs_subset_bool(sub, "use_domain");
1467 if (c_from)
1468 {
1469 return mutt_addr_copy(c_from);
1470 }
1471 else if (c_use_domain)
1472 {
1473 struct Address *addr = mutt_addr_new();
1474 mutt_str_asprintf(&addr->mailbox, "%s@%s", NONULL(Username),
1475 NONULL(mutt_fqdn(true, sub)));
1476 return addr;
1477 }
1478 else
1479 {
1480 return mutt_addr_create(NULL, Username);
1481 }
1482}
1483
1492static int invoke_mta(struct Mailbox *m, struct Email *e, struct ConfigSubset *sub)
1493{
1494 struct Buffer *tempfile = NULL;
1495 int rc = -1;
1496
1497 /* Write out the message in MIME form. */
1498 tempfile = mutt_buffer_pool_get();
1499 mutt_buffer_mktemp(tempfile);
1500 FILE *fp_tmp = mutt_file_fopen(mutt_buffer_string(tempfile), "w");
1501 if (!fp_tmp)
1502 goto cleanup;
1503
1504#ifdef USE_SMTP
1505 const bool c_write_bcc = cs_subset_bool(sub, "write_bcc");
1506 const char *const c_smtp_url = cs_subset_string(sub, "smtp_url");
1507 if (c_smtp_url)
1508 cs_subset_str_native_set(sub, "write_bcc", false, NULL);
1509#endif
1510#ifdef MIXMASTER
1512 !STAILQ_EMPTY(&e->chain),
1514#endif
1515#ifndef MIXMASTER
1517 false, mutt_should_hide_protected_subject(e), sub);
1518#endif
1519#ifdef USE_SMTP
1520 cs_subset_str_native_set(sub, "write_bcc", c_write_bcc, NULL);
1521#endif
1522
1523 fputc('\n', fp_tmp); /* tie off the header. */
1524
1525 if ((mutt_write_mime_body(e->body, fp_tmp, sub) == -1))
1526 goto cleanup;
1527
1528 if (mutt_file_fclose(&fp_tmp) != 0)
1529 {
1531 unlink(mutt_buffer_string(tempfile));
1532 goto cleanup;
1533 }
1534
1535#ifdef MIXMASTER
1536 if (!STAILQ_EMPTY(&e->chain))
1537 {
1538 rc = mix_send_message(&e->chain, mutt_buffer_string(tempfile));
1539 goto cleanup;
1540 }
1541#endif
1542
1543#ifdef USE_NNTP
1544 if (OptNewsSend)
1545 goto sendmail;
1546#endif
1547
1548#ifdef USE_SMTP
1549 if (c_smtp_url)
1550 {
1551 rc = mutt_smtp_send(&e->env->from, &e->env->to, &e->env->cc, &e->env->bcc,
1552 mutt_buffer_string(tempfile),
1553 (e->body->encoding == ENC_8BIT), sub);
1554 goto cleanup;
1555 }
1556#endif
1557
1558sendmail:
1559 rc = mutt_invoke_sendmail(m, &e->env->from, &e->env->to, &e->env->cc,
1560 &e->env->bcc, mutt_buffer_string(tempfile),
1561 (e->body->encoding == ENC_8BIT), sub);
1562cleanup:
1563 if (fp_tmp)
1564 {
1565 mutt_file_fclose(&fp_tmp);
1566 unlink(mutt_buffer_string(tempfile));
1567 }
1568 mutt_buffer_pool_release(&tempfile);
1569 return rc;
1570}
1571
1578void mutt_encode_descriptions(struct Body *b, bool recurse, struct ConfigSubset *sub)
1579{
1580 for (struct Body *t = b; t; t = t->next)
1581 {
1582 if (t->description)
1583 {
1584 const struct Slist *const c_send_charset = cs_subset_slist(sub, "send_charset");
1585 rfc2047_encode(&t->description, NULL, sizeof("Content-Description:"), c_send_charset);
1586 }
1587 if (recurse && t->parts)
1588 mutt_encode_descriptions(t->parts, recurse, sub);
1589 }
1590}
1591
1596static void decode_descriptions(struct Body *b)
1597{
1598 for (struct Body *t = b; t; t = t->next)
1599 {
1600 if (t->description)
1601 {
1602 rfc2047_decode(&t->description);
1603 }
1604 if (t->parts)
1605 decode_descriptions(t->parts);
1606 }
1607}
1608
1613static void fix_end_of_file(const char *data)
1614{
1615 FILE *fp = mutt_file_fopen(data, "a+");
1616 if (!fp)
1617 return;
1618
1619 if ((mutt_file_get_size_fp(fp) > 0) && mutt_file_seek(fp, -1, SEEK_END))
1620 {
1621 int c = fgetc(fp);
1622 if (c != '\n')
1623 fputc('\n', fp);
1624 }
1625 mutt_file_fclose(&fp);
1626}
1627
1638int mutt_resend_message(FILE *fp, struct Mailbox *m, struct Email *e_cur,
1639 struct ConfigSubset *sub)
1640{
1641 struct Email *e_new = email_new();
1642
1643 if (mutt_prepare_template(fp, m, e_new, e_cur, true) < 0)
1644 {
1645 email_free(&e_new);
1646 return -1;
1647 }
1648
1649 if (WithCrypto)
1650 {
1651 /* mutt_prepare_template doesn't always flip on an application bit.
1652 * so fix that here */
1653 if (!(e_new->security & (APPLICATION_SMIME | APPLICATION_PGP)))
1654 {
1655 const bool c_smime_is_default = cs_subset_bool(sub, "smime_is_default");
1656 if (((WithCrypto & APPLICATION_SMIME) != 0) && c_smime_is_default)
1657 e_new->security |= APPLICATION_SMIME;
1658 else if (WithCrypto & APPLICATION_PGP)
1659 e_new->security |= APPLICATION_PGP;
1660 else
1661 e_new->security |= APPLICATION_SMIME;
1662 }
1663
1664 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(sub, "crypt_opportunistic_encrypt");
1665 if (c_crypt_opportunistic_encrypt)
1666 {
1667 e_new->security |= SEC_OPPENCRYPT;
1669 }
1670 }
1671
1672 struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
1673 emaillist_add_email(&el, e_cur);
1674 int rc = mutt_send_message(SEND_RESEND, e_new, NULL, m, &el, sub);
1675 emaillist_clear(&el);
1676
1677 return rc;
1678}
1679
1687static bool is_reply(struct Email *reply, struct Email *orig)
1688{
1689 if (!reply || !reply->env || !orig || !orig->env)
1690 return false;
1691 return mutt_list_find(&orig->env->references, reply->env->message_id) ||
1692 mutt_list_find(&orig->env->in_reply_to, reply->env->message_id);
1693}
1694
1706static bool search_attach_keyword(char *filename, struct ConfigSubset *sub)
1707{
1708 const struct Regex *c_abort_noattach_regex = cs_subset_regex(sub, "abort_noattach_regex");
1709 const struct Regex *c_quote_regex = cs_subset_regex(sub, "quote_regex");
1710
1711 /* Search for the regex in `$abort_noattach_regex` within a file */
1712 if (!c_abort_noattach_regex || !c_abort_noattach_regex->regex ||
1713 !c_quote_regex || !c_quote_regex->regex)
1714 {
1715 return false;
1716 }
1717
1718 FILE *fp_att = mutt_file_fopen(filename, "r");
1719 if (!fp_att)
1720 return false;
1721
1722 char *inputline = mutt_mem_malloc(1024);
1723 bool found = false;
1724 while (!feof(fp_att))
1725 {
1726 fgets(inputline, 1024, fp_att);
1727 if (!mutt_is_quote_line(inputline, NULL) &&
1728 mutt_regex_match(c_abort_noattach_regex, inputline))
1729 {
1730 found = true;
1731 break;
1732 }
1733 }
1734 FREE(&inputline);
1735 mutt_file_fclose(&fp_att);
1736 return found;
1737}
1738
1752static int save_fcc(struct Mailbox *m, struct Email *e, struct Buffer *fcc,
1753 struct Body *clear_content, char *pgpkeylist,
1754 SendFlags flags, char **finalpath, struct ConfigSubset *sub)
1755{
1756 int rc = 0;
1757 struct Body *save_content = NULL;
1758
1760
1761 /* Don't save a copy when we are in batch-mode, and the FCC
1762 * folder is on an IMAP server: This would involve possibly lots
1763 * of user interaction, which is not available in batch mode.
1764 *
1765 * Note: A patch to fix the problems with the use of IMAP servers
1766 * from non-curses mode is available from Brendan Cully. However,
1767 * I'd like to think a bit more about this before including it. */
1768
1769#ifdef USE_IMAP
1770 if ((flags & SEND_BATCH) && !mutt_buffer_is_empty(fcc) &&
1772 {
1773 mutt_error(_("Warning: Fcc to an IMAP mailbox is not supported in batch mode"));
1774 /* L10N: Printed after the "Fcc to an IMAP mailbox is not supported" message.
1775 To make it clearer that the message doesn't mean NeoMutt is aborting
1776 sending the mail too.
1777 %s is the full mailbox URL, including imap(s)://
1778 */
1779 mutt_error(_("Skipping Fcc to %s"), mutt_buffer_string(fcc));
1780 mutt_buffer_reset(fcc);
1781 return rc;
1782 }
1783#endif
1784
1785 if (mutt_buffer_is_empty(fcc) || mutt_str_equal("/dev/null", mutt_buffer_string(fcc)))
1786 return rc;
1787
1788 struct Body *tmpbody = e->body;
1789 struct Body *save_sig = NULL;
1790 struct Body *save_parts = NULL;
1791
1792 const bool c_fcc_before_send = cs_subset_bool(sub, "fcc_before_send");
1793 /* Before sending, we don't allow message manipulation because it
1794 * will break message signatures. This is especially complicated by
1795 * Protected Headers. */
1796 if (!c_fcc_before_send)
1797 {
1798 const bool c_fcc_clear = cs_subset_bool(sub, "fcc_clear");
1799 if ((WithCrypto != 0) &&
1800 (e->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT)) && c_fcc_clear)
1801 {
1802 e->body = clear_content;
1805 mutt_param_delete(&e->body->parameter, "protected-headers");
1806 }
1807
1808 const enum QuadOption c_fcc_attach = cs_subset_quad(sub, "fcc_attach");
1809
1810 /* check to see if the user wants copies of all attachments */
1811 bool save_atts = true;
1812 if (e->body->type == TYPE_MULTIPART)
1813 {
1814 /* In batch mode, save attachments if the quadoption is yes or ask-yes */
1815 if (flags & SEND_BATCH)
1816 {
1817 if ((c_fcc_attach == MUTT_NO) || (c_fcc_attach == MUTT_ASKNO))
1818 save_atts = false;
1819 }
1820 else if (query_quadoption(c_fcc_attach, _("Save attachments in Fcc?")) != MUTT_YES)
1821 save_atts = false;
1822 }
1823 if (!save_atts)
1824 {
1825 if ((WithCrypto != 0) && (e->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT)) &&
1826 (mutt_str_equal(e->body->subtype, "encrypted") ||
1827 mutt_str_equal(e->body->subtype, "signed")))
1828 {
1829 if ((clear_content->type == TYPE_MULTIPART) &&
1830 (query_quadoption(c_fcc_attach, _("Save attachments in Fcc?")) != MUTT_YES))
1831 {
1832 if (!(e->security & SEC_ENCRYPT) && (e->security & SEC_SIGN))
1833 {
1834 /* save initial signature and attachments */
1835 save_sig = e->body->parts->next;
1836 save_parts = clear_content->parts->next;
1837 }
1838
1839 /* this means writing only the main part */
1840 e->body = clear_content->parts;
1841
1842 if (mutt_protect(e, pgpkeylist, false) == -1)
1843 {
1844 /* we can't do much about it at this point, so
1845 * fallback to saving the whole thing to fcc */
1846 e->body = tmpbody;
1847 save_sig = NULL;
1848 goto full_fcc;
1849 }
1850
1851 save_content = e->body;
1852 }
1853 }
1854 else
1855 {
1856 if (query_quadoption(c_fcc_attach, _("Save attachments in Fcc?")) != MUTT_YES)
1857 e->body = e->body->parts;
1858 }
1859 }
1860 }
1861
1862full_fcc:
1863 if (e->body)
1864 {
1865 /* update received time so that when storing to a mbox-style folder
1866 * the From_ line contains the current time instead of when the
1867 * message was first postponed. */
1869 rc = mutt_write_multiple_fcc(mutt_buffer_string(fcc), e, NULL, false, NULL,
1870 finalpath, sub);
1871 while (rc && !(flags & SEND_BATCH))
1872 {
1874 int choice = mutt_multi_choice(
1875 /* L10N: Called when saving to $record or Fcc failed after sending.
1876 (r)etry tries the same mailbox again.
1877 alternate (m)ailbox prompts for a different mailbox to try.
1878 (s)kip aborts saving. */
1879 _("Fcc failed. (r)etry, alternate (m)ailbox, or (s)kip?"),
1880 /* L10N: These correspond to the "Fcc failed" multi-choice prompt
1881 (r)etry, alternate (m)ailbox, or (s)kip.
1882 Any similarity to famous leaders of the FSF is coincidental. */
1883 _("rms"));
1884 switch (choice)
1885 {
1886 case 2: /* alternate (m)ailbox */
1887 /* L10N: This is the prompt to enter an "alternate (m)ailbox" when the
1888 initial Fcc fails. */
1889 rc = mutt_buffer_enter_fname(_("Fcc mailbox"), fcc, true, m, false,
1890 NULL, NULL, MUTT_SEL_NO_FLAGS);
1891 if ((rc == -1) || mutt_buffer_is_empty(fcc))
1892 {
1893 rc = 0;
1894 break;
1895 }
1896 /* fall through */
1897
1898 case 1: /* (r)etry */
1899 rc = mutt_write_multiple_fcc(mutt_buffer_string(fcc), e, NULL, false,
1900 NULL, finalpath, sub);
1901 break;
1902
1903 case -1: /* abort */
1904 case 3: /* (s)kip */
1905 rc = 0;
1906 break;
1907 }
1908 }
1909 }
1910
1911 if (!c_fcc_before_send)
1912 {
1913 e->body = tmpbody;
1914
1915 if ((WithCrypto != 0) && save_sig)
1916 {
1917 /* cleanup the second signature structures */
1918 if (save_content->parts)
1919 {
1920 mutt_body_free(&save_content->parts->next);
1921 save_content->parts = NULL;
1922 }
1923 mutt_body_free(&save_content);
1924
1925 /* restore old signature and attachments */
1926 e->body->parts->next = save_sig;
1927 e->body->parts->parts->next = save_parts;
1928 }
1929 else if ((WithCrypto != 0) && save_content)
1930 {
1931 /* destroy the new encrypted body. */
1932 mutt_body_free(&save_content);
1933 }
1934 }
1935
1936 return 0;
1937}
1938
1949static int postpone_message(struct Email *e_post, struct Email *e_cur,
1950 const char *fcc, SendFlags flags, struct ConfigSubset *sub)
1951{
1952 char *pgpkeylist = NULL;
1953 const char *encrypt_as = NULL;
1954 struct Body *clear_content = NULL;
1955
1956 const char *const c_postponed = cs_subset_string(sub, "postponed");
1957 if (!c_postponed)
1958 {
1959 mutt_error(_("Can't postpone. $postponed is unset"));
1960 return -1;
1961 }
1962
1963 if (e_post->body->next)
1964 e_post->body = mutt_make_multipart(e_post->body);
1965
1966 mutt_encode_descriptions(e_post->body, true, sub);
1967
1968 const bool c_postpone_encrypt = cs_subset_bool(sub, "postpone_encrypt");
1969 if ((WithCrypto != 0) && c_postpone_encrypt &&
1970 (e_post->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)))
1971 {
1972 if (((WithCrypto & APPLICATION_PGP) != 0) && (e_post->security & APPLICATION_PGP))
1973 {
1974 const char *const c_pgp_default_key = cs_subset_string(sub, "pgp_default_key");
1975 encrypt_as = c_pgp_default_key;
1976 }
1977 else if (((WithCrypto & APPLICATION_SMIME) != 0) && (e_post->security & APPLICATION_SMIME))
1978 {
1979 const char *const c_smime_default_key = cs_subset_string(sub, "smime_default_key");
1980 encrypt_as = c_smime_default_key;
1981 }
1982 if (!encrypt_as)
1983 {
1984 const char *const c_postpone_encrypt_as = cs_subset_string(sub, "postpone_encrypt_as");
1985 encrypt_as = c_postpone_encrypt_as;
1986 }
1987
1988#ifdef USE_AUTOCRYPT
1989 if (e_post->security & SEC_AUTOCRYPT)
1990 {
1992 {
1993 if (mutt_istr_equal(e_post->body->subtype, "mixed"))
1994 e_post->body = mutt_remove_multipart(e_post->body);
1995 decode_descriptions(e_post->body);
1996 mutt_error(_("Error encrypting message. Check your crypt settings."));
1997 return -1;
1998 }
1999 encrypt_as = AutocryptDefaultKey;
2000 }
2001#endif
2002
2003 if (encrypt_as)
2004 {
2005 pgpkeylist = mutt_str_dup(encrypt_as);
2006 clear_content = e_post->body;
2007 if (mutt_protect(e_post, pgpkeylist, true) == -1)
2008 {
2009 FREE(&pgpkeylist);
2010 if (mutt_istr_equal(e_post->body->subtype, "mixed"))
2011 e_post->body = mutt_remove_multipart(e_post->body);
2012 decode_descriptions(e_post->body);
2013 mutt_error(_("Error encrypting message. Check your crypt settings."));
2014 return -1;
2015 }
2016
2017 FREE(&pgpkeylist);
2018
2019 mutt_encode_descriptions(e_post->body, false, sub);
2020 }
2021 }
2022
2023 /* make sure the message is written to the right part of a maildir
2024 * postponed folder. */
2025 e_post->read = false;
2026 e_post->old = false;
2027
2028 mutt_prepare_envelope(e_post->env, false, sub);
2029 mutt_env_to_intl(e_post->env, NULL, NULL); /* Handle bad IDNAs the next time. */
2030
2031 if (mutt_write_fcc(NONULL(c_postponed), e_post,
2032 (e_cur && (flags & SEND_REPLY)) ? e_cur->env->message_id : NULL,
2033 true, fcc, NULL, sub) < 0)
2034 {
2035 if (clear_content)
2036 {
2037 mutt_body_free(&e_post->body);
2038 e_post->body = clear_content;
2039 }
2040 mutt_env_free(&e_post->body->mime_headers); /* protected headers */
2041 mutt_param_delete(&e_post->body->parameter, "protected-headers");
2042 if (mutt_istr_equal(e_post->body->subtype, "mixed"))
2043 e_post->body = mutt_remove_multipart(e_post->body);
2044 decode_descriptions(e_post->body);
2046 return -1;
2047 }
2048
2050
2051 if (clear_content)
2052 mutt_body_free(&clear_content);
2053
2054 return 0;
2055}
2056
2063static bool is_text_plain(const struct Body *b)
2064{
2065 return (b->type == TYPE_TEXT) && mutt_istr_equal(b->subtype, "plain");
2066}
2067
2074static bool abort_for_missing_attachments(const struct Body *b, struct ConfigSubset *sub)
2075{
2076 const enum QuadOption c_abort_noattach = cs_subset_quad(sub, "abort_noattach");
2077
2078 if (c_abort_noattach == MUTT_NO)
2079 return false;
2080
2081 if (b->next)
2082 return false;
2083
2084 bool has_keyword = false;
2085
2086 /* search text/plain parts, whether they are main or alternative parts */
2087 if (is_text_plain(b))
2088 {
2089 has_keyword |= search_attach_keyword(b->filename, sub);
2090 }
2091 else
2092 {
2093 for (b = b->parts; b; b = b->next)
2094 {
2095 if (is_text_plain(b))
2096 {
2097 has_keyword |= search_attach_keyword(b->filename, sub);
2098 }
2099 }
2100 }
2101
2102 if (!has_keyword)
2103 return false;
2104
2105 if (c_abort_noattach == MUTT_YES)
2106 {
2107 mutt_error(_("Message contains text matching \"$abort_noattach_regex\". Not sending."));
2108 return true;
2109 }
2110
2111 return query_quadoption(c_abort_noattach, _("No attachments, cancel sending?")) != MUTT_NO;
2112}
2113
2126int mutt_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile,
2127 struct Mailbox *m, struct EmailList *el, struct ConfigSubset *sub)
2128{
2129 struct Buffer fcc = mutt_buffer_make(0); /* where to copy this message */
2130 FILE *fp_tmp = NULL;
2131 struct Body *pbody = NULL;
2132 int i;
2133 bool free_clear_content = false;
2134
2135 struct Body *clear_content = NULL;
2136 char *pgpkeylist = NULL;
2137 /* save current value of "pgp_sign_as" and "smime_default_key" */
2138 char *pgp_sign_as = NULL;
2139 char *smime_sign_as = NULL;
2140 const char *tag = NULL;
2141 char *err = NULL;
2142 const char *ctype = NULL;
2143 char *finalpath = NULL;
2144 struct EmailNode *en = NULL;
2145 struct Email *e_cur = NULL;
2146
2147 if (el)
2148 en = STAILQ_FIRST(el);
2149 if (en)
2150 e_cur = STAILQ_NEXT(en, entries) ? NULL : en->email;
2151
2152 int rc = -1;
2153
2154#ifdef USE_NNTP
2155 if (flags & SEND_NEWS)
2156 OptNewsSend = true;
2157 else
2158 OptNewsSend = false;
2159#endif
2160
2161 const enum QuadOption c_recall = cs_subset_quad(sub, "recall");
2162
2163 if (!flags && !e_templ && (c_recall != MUTT_NO) && mutt_num_postponed(m, true))
2164 {
2165 /* If the user is composing a new message, check to see if there
2166 * are any postponed messages first. */
2167 enum QuadOption ans = query_quadoption(c_recall, _("Recall postponed message?"));
2168 if (ans == MUTT_ABORT)
2169 return rc;
2170
2171 if (ans == MUTT_YES)
2172 flags |= SEND_POSTPONED;
2173 }
2174
2175 /* Allocate the buffer due to the long lifetime, but
2176 * pre-resize it to ensure there are no NULL data field issues */
2177 mutt_buffer_alloc(&fcc, 1024);
2178
2179 if (flags & SEND_POSTPONED)
2180 {
2182 {
2183 const char *const c_pgp_sign_as = cs_subset_string(sub, "pgp_sign_as");
2184 pgp_sign_as = mutt_str_dup(c_pgp_sign_as);
2185 }
2187 {
2188 const char *const c_smime_sign_as = cs_subset_string(sub, "smime_sign_as");
2189 smime_sign_as = mutt_str_dup(c_smime_sign_as);
2190 }
2191 }
2192
2193 /* Delay expansion of aliases until absolutely necessary--shouldn't
2194 * be necessary unless we are prompting the user or about to execute a
2195 * send-hook. */
2196
2197 if (!e_templ)
2198 {
2199 e_templ = email_new();
2200
2201 if (flags == SEND_POSTPONED)
2202 {
2203 rc = mutt_get_postponed(m, e_templ, &e_cur, &fcc);
2204 if (rc < 0)
2205 {
2206 flags = SEND_POSTPONED;
2207 goto cleanup;
2208 }
2209 flags = rc;
2210#ifdef USE_NNTP
2211 /* If postponed message is a news article, it have
2212 * a "Newsgroups:" header line, then set appropriate flag. */
2213 if (e_templ->env->newsgroups)
2214 {
2215 flags |= SEND_NEWS;
2216 OptNewsSend = true;
2217 }
2218 else
2219 {
2220 flags &= ~SEND_NEWS;
2221 OptNewsSend = false;
2222 }
2223#endif
2224 }
2225
2226 if (flags & (SEND_POSTPONED | SEND_RESEND))
2227 {
2228 struct Body *b = e_templ->body;
2229 while (b->parts)
2230 b = b->parts;
2231 fp_tmp = mutt_file_fopen(b->filename, "a+");
2232 if (!fp_tmp)
2233 {
2235 goto cleanup;
2236 }
2237 }
2238
2239 if (!e_templ->env)
2240 e_templ->env = mutt_env_new();
2241 }
2242
2243 /* Parse and use an eventual list-post header */
2244 if ((flags & SEND_LIST_REPLY) && e_cur && e_cur->env && e_cur->env->list_post)
2245 {
2246 /* Use any list-post header as a template */
2247 mutt_parse_mailto(e_templ->env, NULL, e_cur->env->list_post);
2248 /* We don't let them set the sender's address. */
2249 mutt_addrlist_clear(&e_templ->env->from);
2250 }
2251
2252 if (!(flags & (SEND_KEY | SEND_POSTPONED | SEND_RESEND)))
2253 {
2254 /* When SEND_DRAFT_FILE is set, the caller has already
2255 * created the "parent" body structure. */
2256 if (!(flags & SEND_DRAFT_FILE))
2257 {
2258 pbody = mutt_body_new();
2259 pbody->next = e_templ->body; /* don't kill command-line attachments */
2260 e_templ->body = pbody;
2261
2262 const char *const c_content_type = cs_subset_string(sub, "content_type");
2263 ctype = c_content_type;
2264 if (!ctype)
2265 ctype = "text/plain";
2266 mutt_parse_content_type(ctype, e_templ->body);
2267 e_templ->body->unlink = true;
2268 e_templ->body->use_disp = false;
2269 e_templ->body->disposition = DISP_INLINE;
2270
2271 if (tempfile)
2272 {
2273 fp_tmp = mutt_file_fopen(tempfile, "a+");
2274 e_templ->body->filename = mutt_str_dup(tempfile);
2275 if (flags & SEND_NO_FREE_HEADER)
2276 e_templ->body->unlink = false;
2277 }
2278 else
2279 {
2280 char buf[1024] = { 0 };
2281 mutt_mktemp(buf, sizeof(buf));
2282 fp_tmp = mutt_file_fopen(buf, "w+");
2283 e_templ->body->filename = mutt_str_dup(buf);
2284 }
2285 }
2286 else
2287 {
2288 struct Body *b = e_templ->body;
2289 while (b->parts)
2290 b = b->parts;
2291 fp_tmp = mutt_file_fopen(b->filename, "a+");
2292 }
2293
2294 if (!fp_tmp)
2295 {
2296 mutt_debug(LL_DEBUG1, "can't create tempfile %s (errno=%d)\n",
2297 e_templ->body->filename, errno);
2298 mutt_perror(e_templ->body->filename);
2299 goto cleanup;
2300 }
2301 }
2302
2303 const bool c_reverse_name = cs_subset_bool(sub, "reverse_name");
2304 /* this is handled here so that the user can match ~f in send-hook */
2305 if (e_cur && c_reverse_name && !(flags & (SEND_POSTPONED | SEND_RESEND)))
2306 {
2307 /* We shouldn't have to worry about alias expansion here since we are
2308 * either replying to a real or postponed message, therefore no aliases
2309 * should exist since the user has not had the opportunity to add
2310 * addresses to the list. We just have to ensure the postponed messages
2311 * have their aliases expanded. */
2312
2313 if (!TAILQ_EMPTY(&e_templ->env->from))
2314 {
2315 mutt_debug(LL_DEBUG5, "e_templ->env->from before set_reverse_name: %s\n",
2316 TAILQ_FIRST(&e_templ->env->from)->mailbox);
2317 mutt_addrlist_clear(&e_templ->env->from);
2318 }
2319 set_reverse_name(&e_templ->env->from, e_cur->env, sub);
2320 }
2321
2322 const bool c_reply_with_xorig = cs_subset_bool(sub, "reply_with_xorig");
2323 if (e_cur && c_reply_with_xorig && !(flags & (SEND_POSTPONED | SEND_RESEND | SEND_FORWARD)))
2324 {
2325 /* We shouldn't have to worry about freeing 'e_templ->env->from' before
2326 * setting it here since this code will only execute when doing some
2327 * sort of reply. The pointer will only be set when using the -H command
2328 * line option.
2329 *
2330 * If there is already a from address recorded in 'e_templ->env->from',
2331 * then it theoretically comes from `$reverse_name` handling, and we don't use
2332 * the 'X-Orig-To header'. */
2333 if (!TAILQ_EMPTY(&e_cur->env->x_original_to) && TAILQ_EMPTY(&e_templ->env->from))
2334 {
2335 mutt_addrlist_copy(&e_templ->env->from, &e_cur->env->x_original_to, false);
2336 mutt_debug(LL_DEBUG5, "e_templ->env->from extracted from X-Original-To: header: %s\n",
2337 TAILQ_FIRST(&e_templ->env->from)->mailbox);
2338 }
2339 }
2340
2341 const bool c_resume_draft_files = cs_subset_bool(sub, "resume_draft_files");
2342 if (!(flags & (SEND_POSTPONED | SEND_RESEND)) &&
2343 !((flags & SEND_DRAFT_FILE) && c_resume_draft_files))
2344 {
2345 if ((flags & (SEND_REPLY | SEND_FORWARD | SEND_TO_SENDER)) &&
2346 (envelope_defaults(e_templ->env, el, flags, sub) == -1))
2347 {
2348 goto cleanup;
2349 }
2350
2351 const bool c_hdrs = cs_subset_bool(sub, "hdrs");
2352 if (c_hdrs)
2353 process_user_recips(e_templ->env);
2354
2355 /* Expand aliases and remove duplicates/crossrefs */
2356 mutt_expand_aliases_env(e_templ->env);
2357
2358 if (flags & SEND_REPLY)
2359 mutt_fix_reply_recipients(e_templ->env, sub);
2360
2361#ifdef USE_NNTP
2362 if ((flags & SEND_NEWS) && (m && m->type == MUTT_NNTP) && !e_templ->env->newsgroups)
2363 {
2364 e_templ->env->newsgroups = mutt_str_dup(((struct NntpMboxData *) m->mdata)->group);
2365 }
2366#endif
2367
2368 const bool c_auto_edit = cs_subset_bool(sub, "auto_edit");
2369 const bool c_edit_headers = cs_subset_bool(sub, "edit_headers");
2370 const bool c_fast_reply = cs_subset_bool(sub, "fast_reply");
2371 if (!(flags & SEND_BATCH) && !(c_auto_edit && c_edit_headers) &&
2372 !((flags & SEND_REPLY) && c_fast_reply))
2373 {
2374 if (edit_envelope(e_templ->env, flags, sub) == -1)
2375 goto cleanup;
2376 }
2377
2378 /* the from address must be set here regardless of whether or not
2379 * $use_from is set so that the '~P' (from you) operator in send-hook
2380 * patterns will work. if $use_from is unset, the from address is killed
2381 * after send-hooks are evaluated */
2382
2383 const bool killfrom = TAILQ_EMPTY(&e_templ->env->from);
2384 if (killfrom)
2385 {
2387 }
2388
2389 if ((flags & SEND_REPLY) && e_cur)
2390 {
2391 /* change setting based upon message we are replying to */
2393
2394 /* set the replied flag for the message we are generating so that the
2395 * user can use ~Q in a send-hook to know when reply-hook's are also
2396 * being used. */
2397 e_templ->replied = true;
2398 }
2399
2400 /* change settings based upon recipients */
2401
2402 mutt_message_hook(NULL, e_templ, MUTT_SEND_HOOK);
2403
2404 /* Unset the replied flag from the message we are composing since it is
2405 * no longer required. This is done here because the FCC'd copy of
2406 * this message was erroneously get the 'R'eplied flag when stored in
2407 * a maildir-style mailbox. */
2408 e_templ->replied = false;
2409
2410 /* $use_from and/or $from might have changed in a send-hook */
2411 if (killfrom)
2412 {
2413 mutt_addrlist_clear(&e_templ->env->from);
2414
2415 const bool c_use_from = cs_subset_bool(sub, "use_from");
2416 if (c_use_from && !(flags & (SEND_POSTPONED | SEND_RESEND)))
2418 }
2419
2420 if (c_hdrs)
2421 process_user_header(e_templ->env);
2422
2423 if (flags & SEND_BATCH)
2424 {
2425 if (mutt_file_copy_stream(stdin, fp_tmp) < 1)
2426 {
2427 mutt_error(_("Refusing to send an empty email"));
2428 mutt_message(_("Try: echo | neomutt -s 'subject' user@example.com"));
2429 goto cleanup;
2430 }
2431 }
2432
2433 if (!(flags & SEND_BATCH))
2434 mutt_make_greeting(e_templ, fp_tmp, sub);
2435
2436 const bool c_sig_on_top = cs_subset_bool(sub, "sig_on_top");
2437 const char *const c_editor = cs_subset_string(sub, "editor");
2438 if (c_sig_on_top && !(flags & (SEND_KEY | SEND_BATCH)) && c_editor)
2439 {
2440 append_signature(fp_tmp, sub);
2441 }
2442
2443 /* include replies/forwarded messages, unless we are given a template */
2444 if (!tempfile && (m || !(flags & (SEND_REPLY | SEND_FORWARD))) &&
2445 (generate_body(fp_tmp, e_templ, flags, m, el, sub) == -1))
2446 {
2447 goto cleanup;
2448 }
2449
2450 if (!c_sig_on_top && !(flags & (SEND_KEY | SEND_BATCH)) && c_editor)
2451 {
2452 append_signature(fp_tmp, sub);
2453 }
2454 }
2455
2456 /* Only set format=flowed for new messages. postponed/resent/draftfiles
2457 * should respect the original email.
2458 *
2459 * This is set here so that send-hook can be used to turn the option on. */
2460 if (!(flags & (SEND_KEY | SEND_POSTPONED | SEND_RESEND | SEND_DRAFT_FILE)))
2461 {
2462 const bool c_text_flowed = cs_subset_bool(sub, "text_flowed");
2463 if (c_text_flowed && is_text_plain(e_templ->body))
2464 {
2465 mutt_param_set(&e_templ->body->parameter, "format", "flowed");
2466 }
2467 }
2468
2469 /* This hook is even called for postponed messages, and can, e.g., be used
2470 * for setting the editor, the sendmail path, or the envelope sender. */
2471 mutt_message_hook(NULL, e_templ, MUTT_SEND2_HOOK);
2472
2473 /* wait until now to set the real name portion of our return address so
2474 * that $real_name can be set in a send-hook */
2475 {
2476 struct Address *from = TAILQ_FIRST(&e_templ->env->from);
2477 if (from && !from->personal && !(flags & (SEND_RESEND | SEND_POSTPONED)))
2478 {
2479 const char *const c_real_name = cs_subset_string(sub, "real_name");
2480 from->personal = mutt_str_dup(c_real_name);
2481 }
2482 }
2483
2484 if (!(((WithCrypto & APPLICATION_PGP) != 0) && (flags & SEND_KEY)))
2485 mutt_file_fclose(&fp_tmp);
2486
2487 if (!(flags & SEND_BATCH))
2488 {
2489 struct stat st = { 0 };
2490 time_t mtime;
2491 struct Body *b = e_templ->body;
2492 while (b->parts)
2493 b = b->parts;
2494 mtime = mutt_file_decrease_mtime(b->filename, NULL);
2495 if (mtime == (time_t) -1)
2496 {
2498 goto cleanup;
2499 }
2500
2501 mutt_update_encoding(b, sub);
2502
2503 const bool c_edit_headers = cs_subset_bool(sub, "edit_headers");
2504 const bool c_auto_edit = cs_subset_bool(sub, "auto_edit");
2505 const enum QuadOption c_forward_edit = cs_subset_quad(sub, "forward_edit");
2506
2507 /* Select whether or not the user's editor should be called now. We
2508 * don't want to do this when:
2509 * 1) we are sending a key/cert
2510 * 2) we are forwarding a message and the user doesn't want to edit it.
2511 * This is controlled by the quadoption $forward_edit. However, if
2512 * both $edit_headers and $auto_edit are set, we want to ignore the
2513 * setting of $forward_edit because the user probably needs to add the
2514 * recipients. */
2515 if (!(flags & SEND_KEY) &&
2516 (((flags & SEND_FORWARD) == 0) || (c_edit_headers && c_auto_edit) ||
2517 (query_quadoption(c_forward_edit, _("Edit forwarded message?")) == MUTT_YES)))
2518 {
2519 /* If the this isn't a text message, look for a mailcap edit command */
2520 const char *const c_editor = cs_subset_string(sub, "editor");
2521 b = e_templ->body;
2522 while (b->parts)
2523 b = b->parts;
2524 if (mutt_needs_mailcap(b))
2525 {
2526 if (!mutt_edit_attachment(b))
2527 goto cleanup;
2528 }
2529 else if (c_edit_headers)
2530 {
2531 mutt_env_to_local(e_templ->env);
2532 mutt_edit_headers(c_editor, b->filename, e_templ, &fcc);
2533 mutt_env_to_intl(e_templ->env, NULL, NULL);
2534 }
2535 else
2536 {
2537 mutt_edit_file(c_editor, b->filename);
2538 if (stat(b->filename, &st) == 0)
2539 {
2540 if (mtime != st.st_mtime)
2542 }
2543 else
2544 {
2546 }
2547 }
2548
2549 mutt_message_hook(NULL, e_templ, MUTT_SEND2_HOOK);
2550 }
2551
2553 {
2554 if (stat(e_templ->body->filename, &st) == 0)
2555 {
2556 const enum QuadOption c_abort_unmodified = cs_subset_quad(sub, "abort_unmodified");
2557
2558 /* if the file was not modified, bail out now */
2559 if ((mtime == st.st_mtime) && !e_templ->body->next &&
2560 (query_quadoption(c_abort_unmodified, _("Abort unmodified message?")) == MUTT_YES))
2561 {
2562 mutt_message(_("Aborted unmodified message"));
2563 goto cleanup;
2564 }
2565 }
2566 else
2567 mutt_perror(e_templ->body->filename);
2568 }
2569 }
2570
2571 /* Set the message security unless:
2572 * 1) crypto support is not enabled (WithCrypto==0)
2573 * 2) pgp: header field was present during message editing with $edit_headers (e_templ->security != 0)
2574 * 3) we are resending a message
2575 * 4) we are recalling a postponed message (don't override the user's saved settings)
2576 * 5) we are in batch mode
2577 *
2578 * This is done after allowing the user to edit the message so that security
2579 * settings can be configured with send2-hook and $edit_headers. */
2580 if ((WithCrypto != 0) && (e_templ->security == 0) &&
2581 !(flags & (SEND_BATCH | SEND_POSTPONED | SEND_RESEND)))
2582 {
2583 bool c_autocrypt = false;
2584 bool c_autocrypt_reply = false;
2585
2586#ifdef USE_AUTOCRYPT
2587 c_autocrypt = cs_subset_bool(sub, "autocrypt");
2588 c_autocrypt_reply = cs_subset_bool(sub, "autocrypt_reply");
2589#endif
2590
2591 if (c_autocrypt && c_autocrypt_reply && e_cur && (e_cur->security & SEC_AUTOCRYPT))
2592 {
2594 }
2595 else
2596 {
2597 const bool c_crypt_auto_sign = cs_subset_bool(sub, "crypt_auto_sign");
2598 const bool c_crypt_auto_encrypt = cs_subset_bool(sub, "crypt_auto_encrypt");
2599 const bool c_crypt_reply_encrypt = cs_subset_bool(sub, "crypt_reply_encrypt");
2600 const bool c_crypt_reply_sign = cs_subset_bool(sub, "crypt_reply_sign");
2601 const bool c_crypt_reply_sign_encrypted = cs_subset_bool(sub, "crypt_reply_sign_encrypted");
2602
2603 if (c_crypt_auto_sign)
2604 e_templ->security |= SEC_SIGN;
2605 if (c_crypt_auto_encrypt)
2606 e_templ->security |= SEC_ENCRYPT;
2607 if (c_crypt_reply_encrypt && e_cur && (e_cur->security & SEC_ENCRYPT))
2608 e_templ->security |= SEC_ENCRYPT;
2609 if (c_crypt_reply_sign && e_cur && (e_cur->security & SEC_SIGN))
2610 e_templ->security |= SEC_SIGN;
2611 if (c_crypt_reply_sign_encrypted && e_cur && (e_cur->security & SEC_ENCRYPT))
2612 e_templ->security |= SEC_SIGN;
2613
2614 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(sub, "crypt_opportunistic_encrypt");
2615
2616 if (((WithCrypto & APPLICATION_PGP) != 0) &&
2617 ((e_templ->security & (SEC_ENCRYPT | SEC_SIGN)) || c_crypt_opportunistic_encrypt))
2618 {
2619 const bool c_pgp_auto_inline = cs_subset_bool(sub, "pgp_auto_inline");
2620 const bool c_pgp_reply_inline = cs_subset_bool(sub, "pgp_reply_inline");
2621
2622 if (c_pgp_auto_inline)
2623 e_templ->security |= SEC_INLINE;
2624 if (c_pgp_reply_inline && e_cur && (e_cur->security & SEC_INLINE))
2625 e_templ->security |= SEC_INLINE;
2626 }
2627 }
2628
2629 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(sub, "crypt_opportunistic_encrypt");
2630
2631 if (e_templ->security || c_crypt_opportunistic_encrypt)
2632 {
2633 const bool c_crypt_auto_pgp = cs_subset_bool(sub, "crypt_auto_pgp");
2634 const bool c_crypt_auto_smime = cs_subset_bool(sub, "crypt_auto_smime");
2635
2636 /* When replying / forwarding, use the original message's
2637 * crypto system. According to the documentation,
2638 * smime_is_default should be disregarded here.
2639 *
2640 * Problem: At least with forwarding, this doesn't really
2641 * make much sense. Should we have an option to completely
2642 * disable individual mechanisms at run-time? */
2643 if (e_cur)
2644 {
2645 if (((WithCrypto & APPLICATION_PGP) != 0) && c_crypt_auto_pgp &&
2646 (e_cur->security & APPLICATION_PGP))
2647 {
2648 e_templ->security |= APPLICATION_PGP;
2649 }
2650 else if (((WithCrypto & APPLICATION_SMIME) != 0) &&
2651 c_crypt_auto_smime && (e_cur->security & APPLICATION_SMIME))
2652 {
2653 e_templ->security |= APPLICATION_SMIME;
2654 }
2655 }
2656
2657 const bool c_smime_is_default = cs_subset_bool(sub, "smime_is_default");
2658
2659 /* No crypto mechanism selected? Use availability + smime_is_default
2660 * for the decision. */
2661 if (!(e_templ->security & (APPLICATION_SMIME | APPLICATION_PGP)))
2662 {
2663 if (((WithCrypto & APPLICATION_SMIME) != 0) && c_crypt_auto_smime && c_smime_is_default)
2664 {
2665 e_templ->security |= APPLICATION_SMIME;
2666 }
2667 else if (((WithCrypto & APPLICATION_PGP) != 0) && c_crypt_auto_pgp)
2668 {
2669 e_templ->security |= APPLICATION_PGP;
2670 }
2671 else if (((WithCrypto & APPLICATION_SMIME) != 0) && c_crypt_auto_smime)
2672 {
2673 e_templ->security |= APPLICATION_SMIME;
2674 }
2675 }
2676 }
2677
2678 /* opportunistic encrypt relies on SMIME or PGP already being selected */
2679 if (c_crypt_opportunistic_encrypt)
2680 {
2681 /* If something has already enabled encryption, e.g. `$crypt_auto_encrypt`
2682 * or `$crypt_reply_encrypt`, then don't enable opportunistic encrypt for
2683 * the message. */
2684 if (!(e_templ->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)))
2685 {
2686 e_templ->security |= SEC_OPPENCRYPT;
2688 }
2689 }
2690
2691 /* No permissible mechanisms found. Don't sign or encrypt. */
2692 if (!(e_templ->security & (APPLICATION_SMIME | APPLICATION_PGP)))
2693 e_templ->security = SEC_NO_FLAGS;
2694 }
2695
2696 /* Deal with the corner case where the crypto module backend is not available.
2697 * This can happen if configured without PGP/SMIME and with GPGME, but
2698 * $crypt_use_gpgme is unset. */
2699 if (e_templ->security && !crypt_has_module_backend(e_templ->security))
2700 {
2701 mutt_error(_("No crypto backend configured. Disabling message security setting."));
2702 e_templ->security = SEC_NO_FLAGS;
2703 }
2704
2705 /* specify a default fcc. if we are in batchmode, only save a copy of
2706 * the message if the value of $copy is yes or ask-yes */
2707
2708 const enum QuadOption c_copy = cs_subset_quad(sub, "copy");
2709
2710 if (mutt_buffer_is_empty(&fcc) && !(flags & SEND_POSTPONED_FCC) &&
2711 (!(flags & SEND_BATCH) || (c_copy & 0x1)))
2712 {
2713 /* set the default FCC */
2714 const bool killfrom = TAILQ_EMPTY(&e_templ->env->from);
2715 if (killfrom)
2716 {
2718 }
2719 mutt_select_fcc(&fcc, e_templ);
2720 if (killfrom)
2721 {
2722 mutt_addrlist_clear(&e_templ->env->from);
2723 }
2724 }
2725
2726 mutt_rfc3676_space_stuff(e_templ);
2727
2728 mutt_update_encoding(e_templ->body, sub);
2729
2730 if (!(flags & SEND_BATCH))
2731 {
2732 main_loop:
2733
2735 i = mutt_compose_menu(e_templ, &fcc,
2737 sub);
2738 if (i == -1)
2739 {
2740/* abort */
2741#ifdef USE_NNTP
2742 if (flags & SEND_NEWS)
2743 mutt_message(_("Article not posted"));
2744 else
2745#endif
2746 mutt_message(_("Mail not sent"));
2747 goto cleanup;
2748 }
2749 else if (i == 1)
2750 {
2751 if (postpone_message(e_templ, e_cur, mutt_buffer_string(&fcc), flags, sub) != 0)
2752 goto main_loop;
2753 mutt_message(_("Message postponed"));
2754 rc = 1;
2755 goto cleanup;
2756 }
2757 }
2758
2759#ifdef USE_NNTP
2760 if (!(flags & SEND_NEWS))
2761#endif
2762 if ((mutt_addrlist_count_recips(&e_templ->env->to) == 0) &&
2763 (mutt_addrlist_count_recips(&e_templ->env->cc) == 0) &&
2764 (mutt_addrlist_count_recips(&e_templ->env->bcc) == 0))
2765 {
2766 if (flags & SEND_BATCH)
2767 {
2768 puts(_("No recipients specified"));
2769 goto cleanup;
2770 }
2771
2772 mutt_warning(_("No recipients specified"));
2773 goto main_loop;
2774 }
2775
2776 if (mutt_env_to_intl(e_templ->env, &tag, &err))
2777 {
2778 mutt_error(_("Bad IDN in '%s': '%s'"), tag, err);
2779 FREE(&err);
2780 if (flags & SEND_BATCH)
2781 goto cleanup;
2782 goto main_loop;
2783 }
2784
2785 const enum QuadOption c_abort_nosubject = cs_subset_quad(sub, "abort_nosubject");
2786
2787 if (!e_templ->env->subject && !(flags & SEND_BATCH) &&
2788 (query_quadoption(c_abort_nosubject, _("No subject, abort sending?")) != MUTT_NO))
2789 {
2790 /* if the abort is automatic, print an error message */
2791 if (c_abort_nosubject == MUTT_YES)
2792 mutt_error(_("No subject specified"));
2793 goto main_loop;
2794 }
2795#ifdef USE_NNTP
2796 if ((flags & SEND_NEWS) && !e_templ->env->subject)
2797 {
2798 mutt_error(_("No subject specified"));
2799 goto main_loop;
2800 }
2801
2802 if ((flags & SEND_NEWS) && !e_templ->env->newsgroups)
2803 {
2804 mutt_error(_("No newsgroup specified"));
2805 goto main_loop;
2806 }
2807#endif
2808
2809 if (!(flags & SEND_BATCH) && abort_for_missing_attachments(e_templ->body, sub))
2810 {
2811 goto main_loop;
2812 }
2813
2814 if (e_templ->body->next)
2815 e_templ->body = mutt_make_multipart(e_templ->body);
2816
2817 /* Ok, we need to do it this way instead of handling all fcc stuff in
2818 * one place in order to avoid going to main_loop with encoded "env"
2819 * in case of error. Ugh. */
2820
2821 mutt_encode_descriptions(e_templ->body, true, sub);
2822
2823 /* Make sure that clear_content and free_clear_content are
2824 * properly initialized -- we may visit this particular place in
2825 * the code multiple times, including after a failed call to
2826 * mutt_protect(). */
2827
2828 clear_content = NULL;
2829 free_clear_content = false;
2830
2831 if (WithCrypto)
2832 {
2833 if (e_templ->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT))
2834 {
2835 /* save the decrypted attachments */
2836 clear_content = e_templ->body;
2837
2838 if ((crypt_get_keys(e_templ, &pgpkeylist, 0) == -1) ||
2839 (mutt_protect(e_templ, pgpkeylist, false) == -1))
2840 {
2841 if (mutt_istr_equal(e_templ->body->subtype, "mixed"))
2842 e_templ->body = mutt_remove_multipart(e_templ->body);
2843
2844 FREE(&pgpkeylist);
2845
2846 decode_descriptions(e_templ->body);
2847 goto main_loop;
2848 }
2849 mutt_encode_descriptions(e_templ->body, false, sub);
2850 }
2851
2852 /* at this point, e_templ->body is one of the following three things:
2853 * - multipart/signed. In this case, clear_content is a child
2854 * - multipart/encrypted. In this case, clear_content exists independently
2855 * - application/pgp. In this case, clear_content exists independently
2856 * - something else. In this case, it's the same as clear_content */
2857
2858 /* This is ugly -- lack of "reporting back" from mutt_protect(). */
2859
2860 if (clear_content && (e_templ->body != clear_content) &&
2861 (e_templ->body->parts != clear_content))
2862 free_clear_content = true;
2863 }
2864
2865 if (!OptNoCurses)
2866 mutt_message(_("Sending message..."));
2867
2868 mutt_prepare_envelope(e_templ->env, true, sub);
2869
2870 const bool c_fcc_before_send = cs_subset_bool(sub, "fcc_before_send");
2871 if (c_fcc_before_send)
2872 save_fcc(m, e_templ, &fcc, clear_content, pgpkeylist, flags, &finalpath, sub);
2873
2874 i = invoke_mta(m, e_templ, sub);
2875 if (i < 0)
2876 {
2877 if (!(flags & SEND_BATCH))
2878 {
2879 if (!WithCrypto)
2880 ; // do nothing
2881 else if ((e_templ->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)) ||
2882 ((e_templ->security & SEC_SIGN) && (e_templ->body->type == TYPE_APPLICATION)))
2883 {
2884 if (e_templ->body != clear_content)
2885 {
2886 mutt_body_free(&e_templ->body); /* destroy PGP data */
2887 e_templ->body = clear_content; /* restore clear text. */
2888 }
2889 }
2890 else if ((e_templ->security & SEC_SIGN) && (e_templ->body->type == TYPE_MULTIPART))
2891 {
2892 mutt_body_free(&e_templ->body->parts->next); /* destroy sig */
2893 if (mutt_istr_equal(e_templ->body->subtype, "mixed"))
2894 e_templ->body = mutt_remove_multipart(e_templ->body);
2895 }
2896
2897 FREE(&pgpkeylist);
2898 mutt_env_free(&e_templ->body->mime_headers); /* protected headers */
2899 mutt_param_delete(&e_templ->body->parameter, "protected-headers");
2900 if (mutt_istr_equal(e_templ->body->subtype, "mixed"))
2901 e_templ->body = mutt_remove_multipart(e_templ->body);
2902 decode_descriptions(e_templ->body);
2903 mutt_unprepare_envelope(e_templ->env);
2904 FREE(&finalpath);
2905 goto main_loop;
2906 }
2907 else
2908 {
2909 puts(_("Could not send the message"));
2910 goto cleanup;
2911 }
2912 }
2913
2914 if (!c_fcc_before_send)
2915 save_fcc(m, e_templ, &fcc, clear_content, pgpkeylist, flags, &finalpath, sub);
2916
2917 if (!OptNoCurses)
2918 {
2919 mutt_message((i != 0) ? _("Sending in background") :
2920 (flags & SEND_NEWS) ? _("Article posted") : /* USE_NNTP */
2921 _("Mail sent"));
2922#ifdef USE_NOTMUCH
2923 const bool c_nm_record = cs_subset_bool(sub, "nm_record");
2924 if (c_nm_record)
2925 nm_record_message(m, finalpath, e_cur);
2926#endif
2927 mutt_sleep(0);
2928 }
2929
2930 if (WithCrypto)
2931 FREE(&pgpkeylist);
2932
2933 if ((WithCrypto != 0) && free_clear_content)
2934 mutt_body_free(&clear_content);
2935
2936 /* set 'replied' flag only if the user didn't change/remove
2937 * In-Reply-To: and References: headers during edit */
2938 if (flags & SEND_REPLY)
2939 {
2940 if (!(flags & SEND_POSTPONED) && m)
2941 {
2942 STAILQ_FOREACH(en, el, entries)
2943 {
2944 mutt_set_flag(m, en->email, MUTT_REPLIED, is_reply(en->email, e_templ));
2945 }
2946 }
2947 }
2948
2949 rc = 0;
2950
2951cleanup:
2952 mutt_buffer_dealloc(&fcc);
2953
2954 if (flags & SEND_POSTPONED)
2955 {
2957 {
2958 cs_subset_str_string_set(sub, "pgp_sign_as", pgp_sign_as, NULL);
2959 FREE(&pgp_sign_as);
2960 }
2962 {
2963 cs_subset_str_string_set(sub, "smime_sign_as", smime_sign_as, NULL);
2964 FREE(&smime_sign_as);
2965 }
2966 }
2967
2968 mutt_file_fclose(&fp_tmp);
2969 if (!(flags & SEND_NO_FREE_HEADER))
2970 email_free(&e_templ);
2971
2972 FREE(&finalpath);
2973 return rc;
2974}
2975
2986static bool send_simple_email(struct Mailbox *m, struct EmailList *el,
2987 const char *mailto, const char *subj, const char *body)
2988{
2989 struct Email *e = email_new();
2990
2991 /* envelope */
2992 e->env = mutt_env_new();
2993 mutt_parse_mailto(e->env, NULL, mailto);
2994 if (!e->env->subject)
2995 {
2996 e->env->subject = mutt_str_dup(subj);
2997 }
2998 if (TAILQ_EMPTY(&e->env->to) && !mutt_addrlist_parse(&e->env->to, NULL))
2999 {
3000 mutt_warning(_("No recipient specified"));
3001 }
3002
3003 /* body */
3004 e->body = mutt_body_new();
3005 char ctype[] = "text/plain";
3006 mutt_parse_content_type(ctype, e->body);
3007
3008 char tempfile[PATH_MAX] = { 0 };
3009 mutt_mktemp(tempfile, sizeof(tempfile));
3010 if (body)
3011 {
3012 FILE *fp = mutt_file_fopen(tempfile, "w+");
3013 if (!fp)
3014 {
3015 email_free(&e);
3016 return false;
3017 }
3018 fprintf(fp, "%s\n", body);
3019 mutt_file_fclose(&fp);
3020 }
3021 e->body->filename = mutt_str_dup(tempfile);
3022 e->body->unlink = true;
3023
3024 const int rc = mutt_send_message(SEND_DRAFT_FILE, e, NULL, m, el, NeoMutt->sub);
3025 return rc >= 0;
3026}
3027
3035bool mutt_send_list_subscribe(struct Mailbox *m, struct Email *e)
3036{
3037 if (!e || !e->env)
3038 {
3039 return false;
3040 }
3041
3042 const char *mailto = e->env->list_subscribe;
3043 if (!mailto)
3044 {
3045 mutt_warning(_("No List-Subscribe header found"));
3046 return false;
3047 }
3048
3049 struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3050 emaillist_add_email(&el, e);
3051 bool rc = send_simple_email(m, &el, mailto, "Subscribe", "subscribe");
3052 emaillist_clear(&el);
3053
3054 return rc;
3055}
3056
3064bool mutt_send_list_unsubscribe(struct Mailbox *m, struct Email *e)
3065{
3066 if (!e || !e->env)
3067 {
3068 return false;
3069 }
3070
3071 const char *mailto = e->env->list_unsubscribe;
3072 if (!mailto)
3073 {
3074 mutt_warning(_("No List-Unsubscribe header found"));
3075 return false;
3076 }
3077
3078 struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3079 emaillist_add_email(&el, e);
3080 bool rc = send_simple_email(m, &el, mailto, "Unsubscribe", "unsubscribe");
3081 emaillist_clear(&el);
3082
3083 return rc;
3084}
struct Address * mutt_addr_create(const char *personal, const char *mailbox)
Create and populate a new Address.
Definition: address.c:398
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:737
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1470
void mutt_addr_free(struct Address **ptr)
Free a single Address.
Definition: address.c:440
bool mutt_addr_valid_msgid(const char *msgid)
Is this a valid Message ID?
Definition: address.c:764
bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
Compare two e-mail addresses.
Definition: address.c:864
void mutt_addrlist_append(struct AddressList *al, struct Address *a)
Append an Address to an AddressList.
Definition: address.c:1490
struct Address * mutt_addr_new(void)
Create a new Address.
Definition: address.c:385
size_t mutt_addrlist_write(const struct AddressList *al, char *buf, size_t buflen, bool display)
Write an Address to a buffer.
Definition: address.c:1150
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition: address.c:1388
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:616
struct Address * mutt_addr_copy(const struct Address *addr)
Copy the real address.
Definition: address.c:716
void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
Remove cross-references.
Definition: address.c:1443
int mutt_addrlist_count_recips(const struct AddressList *al)
Count the number of Addresses with valid recipients.
Definition: address.c:844
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:458
void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
Prepend an Address to an AddressList.
Definition: address.c:1501
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition: address.c:1305
bool mutt_addrlist_search(const struct AddressList *haystack, const struct Address *needle)
Search for an e-mail address in a list.
Definition: address.c:881
const char * mutt_addr_for_display(const struct Address *a)
Convert an Address for display purposes.
Definition: address.c:986
void mutt_addrlist_dedupe(struct AddressList *al)
Remove duplicate addresses.
Definition: address.c:1407
Email Address Handling.
Email Aliases.
void mutt_expand_aliases(struct AddressList *al)
Expand aliases in a List of Addresses.
Definition: alias.c:298
bool mutt_addr_is_user(const struct Address *addr)
Does the address belong to the user.
Definition: alias.c:574
void mutt_expand_aliases_env(struct Envelope *env)
Expand aliases in all the fields of an Envelope.
Definition: alias.c:312
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:591
char * AutocryptDefaultKey
Autocrypt default key id (used for postponing messages)
Definition: config.c:36
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:702
Select a Mailbox from a list.
#define MUTT_SEL_NO_FLAGS
No flags are set.
Definition: lib.h:55
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:67
void mutt_buffer_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:275
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:260
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:327
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:309
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:85
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
GUI editor for an email's headers.
#define MUTT_COMPOSE_NOFREEHEADER
Definition: lib.h:50
int mutt_compose_menu(struct Email *e, struct Buffer *fcc, uint8_t flags, struct ConfigSubset *sub)
Allow the user to edit the message envelope.
Definition: compose.c:307
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:243
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition: helpers.c:268
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition: helpers.c:218
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
const struct Address * cs_subset_address(const struct ConfigSubset *sub, const char *name)
Get an Address config item by name.
Definition: helpers.c:49
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
Convenience wrapper for the config headers.
char * HomeDir
User's home directory.
Definition: mutt_globals.h:49
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:864
Duplicate the structure of an entire email.
#define CH_DECODE
Do RFC2047 header decoding.
Definition: copy.h:54
#define MUTT_CM_WEED
Weed message/rfc822 attachment headers.
Definition: copy.h:41
#define MUTT_CM_REPLYING
Replying the message.
Definition: copy.h:44
#define MUTT_CM_PREFIX
Quote the header and body.
Definition: copy.h:37
#define MUTT_CM_DECODE
Decode the message body into text/plain.
Definition: copy.h:38
#define CH_WEED
Weed the headers?
Definition: copy.h:53
#define CH_REORDER
Re-order output of headers (specified by 'hdr_order')
Definition: copy.h:59
#define MUTT_CM_CHARCONV
Perform character set conversions.
Definition: copy.h:42
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition: copy.h:50
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition: copy.h:35
#define MUTT_CM_NOHEADER
Don't copy the message header.
Definition: copy.h:36
uint16_t CopyMessageFlags
Flags for mutt_copy_message(), e.g. MUTT_CM_NOHEADER.
Definition: copy.h:34
Convenience wrapper for the core headers.
void crypt_opportunistic_encrypt(struct Email *e)
Can all recipients be determined.
Definition: crypt.c:1025
bool crypt_valid_passphrase(SecurityFlags flags)
Check that we have a usable passphrase, ask if not.
Definition: crypt.c:134
bool mutt_should_hide_protected_subject(struct Email *e)
Should NeoMutt hide the protected subject?
Definition: crypt.c:1078
int mutt_protect(struct Email *e, char *keylist, bool postpone)
Encrypt and/or sign a message.
Definition: crypt.c:159
int crypt_get_keys(struct Email *e, char **keylist, bool oppenc_mode)
Check we have all the keys we need.
Definition: crypt.c:941
bool crypt_has_module_backend(SecurityFlags type)
Is there a crypto backend for a given type?
Definition: cryptglue.c:171
struct Body * crypt_pgp_make_key_attachment(void)
Wrapper for CryptModuleSpecs::pgp_make_key_attachment()
Definition: cryptglue.c:305
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition: curs_lib.c:312
int mutt_buffer_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:445
void mutt_format_s(char *buf, size_t buflen, const char *prec, const char *s)
Format a simple string.
Definition: curs_lib.c:793
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:428
bool mutt_is_quote_line(char *line, regmatch_t *pmatch)
Is a line of message text a quote?
Definition: display.c:310
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
struct Body * mutt_body_new(void)
Create a new Body.
Definition: body.c:43
int emaillist_add_email(struct EmailList *el, struct Email *e)
Add an Email to a list.
Definition: email.c:159
struct Email * email_new(void)
Create a new Email.
Definition: email.c:78
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:44
void emaillist_clear(struct EmailList *el)
Drop a private list of Emails.
Definition: email.c:138
Structs that make up an email.
Enter a string.
int mutt_buffer_get_field(const char *field, struct Buffer *buf, CompletionFlags complete, bool multiple, struct Mailbox *m, char ***files, int *numfiles)
Ask the user for a string.
Definition: window.c:180
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:326
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition: envelope.c:97
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition: envelope.c:43
void mutt_env_to_local(struct Envelope *env)
Convert an Envelope's Address fields to local format.
Definition: envelope.c:288
bool mutt_is_subscribed_list_recipient(bool all_addr, struct Envelope *env)
Matches subscribed mailing lists.
Definition: exec.c:459
bool mutt_is_list_recipient(bool all_addr, struct Envelope *env)
Matches known mailing lists.
Definition: exec.c:472
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:260
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:618
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition: file.c:1569
time_t mutt_file_decrease_mtime(const char *fp, struct stat *st)
Decrease a file's modification time by 1 second.
Definition: file.c:1019
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:690
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
Flags to control mutt_expando_format()
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
#define MUTT_FORMAT_OPTIONAL
Allow optional field processing.
Definition: format_flags.h:33
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
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
static const char * greeting_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, intptr_t data, MuttFormatFlags flags)
Format a greetings string - Implements format_t -.
Definition: send.c:659
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t callback, intptr_t data, MuttFormatFlags flags)
Expand expandos (x) in a string -.
Definition: muttlib.c:777
#define mutt_warning(...)
Definition: logging.h:85
#define mutt_error(...)
Definition: logging.h:87
#define mutt_message(...)
Definition: logging.h:86
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
#define mutt_perror(...)
Definition: logging.h:88
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe() -.
Definition: imap.c:2400
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:1811
Decide how to display email content.
void mutt_make_string(char *buf, size_t buflen, int cols, const char *s, struct Mailbox *m, int inpgr, struct Email *e, MuttFormatFlags flags, const char *progress)
Create formatted strings using mailbox expandos.
Definition: hdrline.c:1405
String processing routines to generate the mail index.
int mutt_rfc822_write_header(FILE *fp, struct Envelope *env, struct Body *attach, enum MuttWriteHeaderMode mode, bool privacy, bool hide_protected_subject, struct ConfigSubset *sub)
Write out one RFC822 header line.
Definition: header.c:572
@ MUTT_WRITE_HEADER_NORMAL
A normal Email, write full header + MIME headers.
Definition: header.h:40
void mutt_select_fcc(struct Buffer *path, struct Email *e)
Select the FCC path for an email.
Definition: hook.c:776
void mutt_message_hook(struct Mailbox *m, struct Email *e, HookFlags type)
Perform a message hook.
Definition: hook.c:658
Parse and execute user-defined hooks.
#define MUTT_SEND_HOOK
send-hook: when composing a new email
Definition: hook.h:40
#define MUTT_SEND2_HOOK
send2-hook: when changing fields in the compose menu
Definition: hook.h:49
#define MUTT_REPLY_HOOK
reply-hook: when replying to an email
Definition: hook.h:48
#define MUTT_MESSAGE_HOOK
message-hook: run before displaying a message
Definition: hook.h:45
IMAP network mailbox.
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
struct ListNode * mutt_list_find(const struct ListHead *h, const char *data)
Find a string in a List.
Definition: list.c:102
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition: list.c:45
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
@ LL_DEBUG5
Log at debug level 5.
Definition: logging.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition: mailbox.h:49
@ MUTT_IMAP
'IMAP' Mailbox type
Definition: mailbox.h:50
Handle mailing lists.
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define FREE(x)
Definition: memory.h:43
#define mutt_array_size(x)
Definition: memory.h:36
@ 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
Support of Mixmaster anonymous remailer.
int mix_send_message(struct ListHead *chain, const char *tempfile)
Send an email via Mixmaster.
Definition: mixmaster.c:102
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
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool mutt_path_canon(char *buf, size_t buflen, const char *homedir, bool is_dir)
Create the canonical version of a path.
Definition: path.c:285
bool mutt_regex_match(const struct Regex *regex, const char *str)
Shorthand to mutt_regex_capture()
Definition: regex.c:631
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:819
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1031
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:679
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:652
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:239
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:326
Many unsorted constants and some structs.
#define MUTT_COMP_ALIAS
Alias completion (in alias dialog)
Definition: mutt.h:53
#define MUTT_TOKEN_NO_FLAGS
No flags are set.
Definition: mutt.h:67
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:52
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:92
#define PATH_MAX
Definition: mutt.h:40
bool mutt_edit_attachment(struct Body *a)
Edit an attachment.
Definition: mutt_attach.c:260
int mutt_body_copy(FILE *fp, struct Body **tgt, struct Body *src)
Create a send-mode duplicate from a receive-mode body.
Definition: mutt_body.c:48
Representation of the body of an email.
Hundreds of global variables to back the user variables.
struct ListHead UserHeader
List of custom headers to add to outgoing emails.
Definition: mutt_globals.h:66
char * Username
User's login name.
Definition: mutt_globals.h:52
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:172
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_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:599
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:322
bool mutt_needs_mailcap(struct Body *m)
Does this type need a mailcap entry do display.
Definition: muttlib.c:405
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1455
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition: muttlib.c:1311
Some miscellaneous functions.
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
#define mutt_mktemp(buf, buflen)
Definition: muttlib.h:71
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1193
struct Message * mx_msg_open(struct Mailbox *m, int msgno)
Return a stream pointer for a message.
Definition: mx.c:1147
API for mailboxes.
API for encryption/signing of emails.
#define SEC_INLINE
Email has an inline signature.
Definition: lib.h:85
#define SEC_AUTOCRYPT
(Autocrypt) Message will be, or was Autocrypt encrypt+signed
Definition: lib.h:87
#define SEC_OPPENCRYPT
Opportunistic encrypt mode.
Definition: lib.h:86
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:90
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:91
#define SEC_NO_FLAGS
No flags are set.
Definition: lib.h:77
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:78
#define WithCrypto
Definition: lib.h:116
#define SEC_AUTOCRYPT_OVERRIDE
(Autocrypt) Indicates manual set/unset of encryption
Definition: lib.h:88
#define SEC_SIGN
Email is signed.
Definition: lib.h:79
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:1868
Handling of global boolean variables.
bool OptNoCurses
(pseudo) when sending in batch mode
Definition: options.h:53
bool OptNewsSend
(pseudo) used to change behavior when posting
Definition: options.h:51
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:142
void mutt_param_set(struct ParameterList *pl, const char *attribute, const char *value)
Set a Parameter.
Definition: parameter.c:110
void mutt_parse_content_type(const char *s, struct Body *ct)
Parse a content type.
Definition: parse.c:424
bool mutt_parse_mailto(struct Envelope *env, char **body, const char *src)
Parse a mailto:// url.
Definition: parse.c:1671
char * mutt_extract_message_id(const char *s, size_t *len)
Find a message-id.
Definition: parse.c:360
Match patterns to emails.
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
Postponed Emails.
int mutt_num_postponed(struct Mailbox *m, bool force)
Return the number of postponed messages.
Definition: postpone.c:69
void mutt_update_num_postponed(void)
Force the update of the number of postponed messages.
Definition: postpone.c:183
int mutt_get_postponed(struct Mailbox *m_cur, struct Email *hdr, struct Email **cur, struct Buffer *fcc)
Recall a postponed message.
Definition: postpone.c:667
int mutt_prepare_template(FILE *fp, struct Mailbox *m, struct Email *e_new, struct Email *e, bool resend)
Prepare a message template.
Definition: postpone.c:503
Prototypes for many functions.
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:63
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.
int mutt_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question.
Definition: question.c:54
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: question.c:386
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:735
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
#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:723
#define STAILQ_EMPTY(head)
Definition: queue.h:348
#define TAILQ_SWAP(head1, head2, type, field)
Definition: queue.h:859
#define TAILQ_FOREACH_REVERSE(var, head, headname, field)
Definition: queue.h:745
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:841
#define TAILQ_NEXT(elm, field)
Definition: queue.h:832
#define TAILQ_EMPTY(head)
Definition: queue.h:721
#define STAILQ_NEXT(elm, field)
Definition: queue.h:400
void mutt_generate_recvattach_list(struct AttachCtx *actx, struct Email *e, struct Body *parts, FILE *fp, int parent_type, int level, bool decrypted)
Create a list of attachments.
Definition: recvattach.c:1061
void rfc2047_encode(char **pd, const char *specials, int col, const struct Slist *charsets)
RFC-2047-encode a string.
Definition: rfc2047.c:619
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition: rfc2047.c:649
void mutt_rfc3676_space_stuff(struct Email *e)
Perform RFC3676 space stuffing on an Email.
Definition: rfc3676.c:481
RFC3676 Format Flowed routines.
int mutt_write_mime_body(struct Body *a, FILE *fp, struct ConfigSubset *sub)
Write a MIME part.
Definition: body.c:314
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:1949
void mutt_make_post_indent(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add suffix to replied email text.
Definition: send.c:742
static int generate_body(FILE *fp_tmp, struct Email *e, SendFlags flags, struct Mailbox *m, struct EmailList *el, struct ConfigSubset *sub)
Create a new email body.
Definition: send.c:1220
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:764
static bool is_reply(struct Email *reply, struct Email *orig)
Is one email a reply to another?
Definition: send.c:1687
void mutt_encode_descriptions(struct Body *b, bool recurse, struct ConfigSubset *sub)
RFC2047 encode the content-descriptions.
Definition: send.c:1578
void mutt_make_misc_reply_headers(struct Envelope *env, struct Envelope *curenv, struct ConfigSubset *sub)
Set subject for a reply.
Definition: send.c:1063
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:1752
static bool send_simple_email(struct Mailbox *m, struct EmailList *el, const char *mailto, const char *subj, const char *body)
Compose an email given a few basic ingredients.
Definition: send.c:2986
int mutt_edit_address(struct AddressList *al, const char *field, bool expand_aliases)
Edit an email address.
Definition: send.c:178
void mutt_forward_intro(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add the "start of forwarded message" text.
Definition: send.c:449
void mutt_make_forward_subject(struct Envelope *env, struct Email *e, struct ConfigSubset *sub)
Create a subject for a forwarded email.
Definition: send.c:1043
void mutt_add_to_reference_headers(struct Envelope *env, struct Envelope *curenv, struct ConfigSubset *sub)
Generate references for a reply email.
Definition: send.c:1090
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:813
void mutt_fix_reply_recipients(struct Envelope *env, struct ConfigSubset *sub)
Remove duplicate recipients.
Definition: send.c:1013
static char * nntp_get_header(const char *s)
Get the trimmed header.
Definition: send.c:360
int mutt_resend_message(FILE *fp, struct Mailbox *m, struct Email *e_cur, struct ConfigSubset *sub)
Resend an email.
Definition: send.c:1638
static int include_forward(struct Mailbox *m, struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Write out a forwarded message.
Definition: send.c:499
static void remove_user(struct AddressList *al, bool leave_only)
Remove any address which matches the current user.
Definition: send.c:132
static void add_message_id(struct ListHead *head, struct Envelope *env)
Add the email's message ID to a list.
Definition: send.c:1000
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:151
int mutt_fetch_recips(struct Envelope *out, struct Envelope *in, SendFlags flags, struct ConfigSubset *sub)
Generate recpients for a reply email.
Definition: send.c:925
static int envelope_defaults(struct Envelope *env, struct EmailList *el, SendFlags flags, struct ConfigSubset *sub)
Fill in some defaults for a new email.
Definition: send.c:1148
void mutt_make_attribution(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add "on DATE, PERSON wrote" header.
Definition: send.c:633
static void mutt_make_greeting(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add greetings string.
Definition: send.c:721
static int invoke_mta(struct Mailbox *m, struct Email *e, struct ConfigSubset *sub)
Send an email.
Definition: send.c:1492
int mutt_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile, struct Mailbox *m, struct EmailList *el, struct ConfigSubset *sub)
Send an email.
Definition: send.c:2126
struct Address * mutt_default_from(struct ConfigSubset *sub)
Get a default 'from' Address.
Definition: send.c:1459
static void process_user_recips(struct Envelope *env)
Process the user headers.
Definition: send.c:371
bool mutt_send_list_unsubscribe(struct Mailbox *m, struct Email *e)
Send a mailing-list unsubscription email.
Definition: send.c:3064
static void process_user_header(struct Envelope *env)
Process the user headers.
Definition: send.c:398
static int edit_envelope(struct Envelope *en, SendFlags flags, struct ConfigSubset *sub)
Edit Envelope fields.
Definition: send.c:221
static bool abort_for_missing_attachments(const struct Body *b, struct ConfigSubset *sub)
Should we abort sending because of missing attachments?
Definition: send.c:2074
static int default_to(struct AddressList *to, struct Envelope *env, SendFlags flags, int hmfupto, struct ConfigSubset *sub)
Generate default email addresses.
Definition: send.c:839
static void make_reference_headers(struct EmailList *el, struct Envelope *env, struct ConfigSubset *sub)
Generate reference headers for an email.
Definition: send.c:1110
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:1407
static void fix_end_of_file(const char *data)
Ensure a file ends with a linefeed.
Definition: send.c:1613
static bool search_attach_keyword(char *filename, struct ConfigSubset *sub)
Search an email for 'attachment' keywords.
Definition: send.c:1706
void mutt_forward_trailer(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add a "end of forwarded message" text.
Definition: send.c:472
static bool is_text_plain(const struct Body *b)
Is a Body a text/plain MIME part?
Definition: send.c:2063
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:558
bool mutt_send_list_subscribe(struct Mailbox *m, struct Email *e)
Send a mailing-list subscription email.
Definition: send.c:3035
static void add_references(struct ListHead *head, struct Envelope *env)
Add the email's references to a list.
Definition: send.c:984
static void decode_descriptions(struct Body *b)
RFC2047 decode them in case of an error.
Definition: send.c:1596
void mutt_set_followup_to(struct Envelope *env, struct ConfigSubset *sub)
Set followup-to field.
Definition: send.c:1334
static void append_signature(FILE *fp, struct ConfigSubset *sub)
Append a signature to an email.
Definition: send.c:95
Prepare and send an email.
#define SEND_POSTPONED_FCC
Used by mutt_get_postponed() to signal that the x-mutt-fcc header field was present.
Definition: send.h:48
#define SEND_BATCH
Send email in batch mode (without user interaction)
Definition: send.h:45
#define SEND_GROUP_CHAT_REPLY
Reply to all recipients preserving To/Cc.
Definition: send.h:52
#define SEND_NO_FREE_HEADER
Used by the -E flag.
Definition: send.h:49
#define SEND_DRAFT_FILE
Used by the -H flag.
Definition: send.h:50
#define SEND_GROUP_REPLY
Reply to all.
Definition: send.h:41
#define SEND_LIST_REPLY
Reply to mailing list.
Definition: send.h:42
#define SEND_KEY
Mail a PGP public key.
Definition: send.h:46
#define SEND_POSTPONED
Recall a postponed email.
Definition: send.h:44
#define SEND_TO_SENDER
Compose new email to sender.
Definition: send.h:51
#define SEND_RESEND
Reply using the current email as a template.
Definition: send.h:47
#define SEND_REPLY
Reply to sender.
Definition: send.h:40
#define SEND_REVIEW_TO
Allow the user to edit the To field.
Definition: send.h:54
#define SEND_NEWS
Reply to a news article.
Definition: send.h:53
#define SEND_FORWARD
Forward email.
Definition: send.h:43
uint16_t SendFlags
Flags for mutt_send_message(), e.g. SEND_REPLY.
Definition: send.h:38
const char * mutt_fqdn(bool may_hide_host, const struct ConfigSubset *sub)
Get the Fully-Qualified Domain Name.
Definition: sendlib.c:698
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:982
void mutt_unprepare_envelope(struct Envelope *env)
Undo the encodings of mutt_prepare_envelope()
Definition: sendlib.c:793
void mutt_prepare_envelope(struct Envelope *env, bool final, struct ConfigSubset *sub)
Prepare an email header.
Definition: sendlib.c:754
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:445
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:1030
void mutt_update_encoding(struct Body *a, struct ConfigSubset *sub)
Update the encoding type.
Definition: sendlib.c:413
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:292
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:520
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:901
const char * mutt_get_name(const struct Address *a)
Pick the best name to display from an address.
Definition: sort.c:136
Assorted sorting methods.
Key value store.
#define NONULL(x)
Definition: string2.h:37
#define SKIPWS(ch)
Definition: string2.h:46
An email address.
Definition: address.h:36
bool group
Group mailbox?
Definition: address.h:39
char * mailbox
Mailbox and host address.
Definition: address.h:38
char * personal
Real name of address.
Definition: address.h:37
A set of attachments.
Definition: attach.h:51
FILE * fp_root
Used by recvattach for updating.
Definition: attach.h:53
struct Email * email
Used by recvattach for updating.
Definition: attach.h:52
struct AttachPtr ** idx
Array of attachments.
Definition: attach.h:55
short idxlen
Number of attachmentes.
Definition: attach.h:56
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:72
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition: body.h:67
struct Envelope * mime_headers
Memory hole protected headers.
Definition: body.h:75
struct ParameterList parameter
Parameters of the content-type.
Definition: body.h:62
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:71
char * subtype
content-type subtype
Definition: body.h:60
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:58
String manipulation buffer.
Definition: buffer.h:34
size_t dsize
Length of data.
Definition: buffer.h:37
char * data
Pointer to data.
Definition: buffer.h:35
A set of inherited config items.
Definition: subset.h:47
struct ConfigSet * cs
Parent ConfigSet.
Definition: subset.h:51
List of Emails.
Definition: email.h:131
struct Email * email
Email in the list.
Definition: email.h:132
The envelope/body of an email.
Definition: email.h:37
bool read
Email is read.
Definition: email.h:48
struct Envelope * env
Envelope information.
Definition: email.h:66
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib....
Definition: email.h:41
struct Body * body
List of MIME parts.
Definition: email.h:67
bool old
Email is seen, but unread.
Definition: email.h:47
struct ListHead chain
Mixmaster chain.
Definition: email.h:90
bool replied
Email has been replied to.
Definition: email.h:49
int msgno
Number displayed to the user.
Definition: email.h:111
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:59
The header of an Email.
Definition: envelope.h:57
struct ListHead userhdrs
user defined headers
Definition: envelope.h:87
char * list_subscribe
This stores a mailto URL, or nothing.
Definition: envelope.h:68
struct AddressList to
Email's 'To' list.
Definition: envelope.h:60
char * followup_to
List of 'followup-to' fields.
Definition: envelope.h:81
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:82
struct AddressList x_original_to
Email's 'X-Orig-to'.
Definition: envelope.h:66
char * newsgroups
List of newsgroups.
Definition: envelope.h:79
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:85
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:86
char * subject
Email's subject.
Definition: envelope.h:70
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 * 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
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
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: mxapi.h:43
FILE * fp
pointer to the message data
Definition: mxapi.h:44
struct Message::@0 flags
Flags for the Message.
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
NNTP-specific Mailbox data -.
Definition: mdata.h:33
Cached regular expression.
Definition: regex3.h:89
regex_t * regex
compiled expression
Definition: regex3.h:91
String list.
Definition: slist.h:47
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:305
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:408