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