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