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