NeoMutt  2025-12-11-872-g385a04
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
recvcmd.c
Go to the documentation of this file.
1
26
32
33#include "config.h"
34#include <locale.h>
35#include <stdbool.h>
36#include <stdio.h>
37#include "mutt/lib.h"
38#include "address/lib.h"
39#include "config/lib.h"
40#include "email/lib.h"
41#include "core/lib.h"
42#include "alias/lib.h"
43#include "gui/lib.h"
44#include "mutt.h"
45#include "recvcmd.h"
46#include "editor/lib.h"
47#include "expando/lib.h"
48#include "history/lib.h"
49#include "index/lib.h"
50#include "question/lib.h"
51#include "send/lib.h"
52#include "attach.h"
53#include "globals.h"
54#include "mutt_logging.h"
55#ifdef ENABLE_NLS
56#include <libintl.h>
57#endif
58
68static bool check_msg(struct Body *b, bool err)
69{
71 {
72 if (err)
73 mutt_error(_("You may only bounce message/rfc822 parts"));
74 return false;
75 }
76 return true;
77}
78
85static bool check_all_msg(struct AttachPtrArray *aa, bool err)
86{
87 struct AttachPtr **app = NULL;
88 ARRAY_FOREACH(app, aa)
89 {
90 if (!check_msg((*app)->body, err))
91 return false;
92 }
93 return true;
94}
95
101static bool check_can_decode(struct AttachPtrArray *aa)
102{
103 struct AttachPtr **app = NULL;
104 ARRAY_FOREACH(app, aa)
105 {
106 if (!mutt_can_decode((*app)->body))
107 return false;
108 }
109
110 return true;
111}
112
119static bool aa_contains_body(struct AttachPtrArray *aa, const struct Body *b)
120{
121 if (!b)
122 return false;
123
124 struct AttachPtr **app = NULL;
125 ARRAY_FOREACH(app, aa)
126 {
127 if ((*app)->body == b)
128 return true;
129 }
130
131 return false;
132}
133
141static short count_selected_children(struct AttachCtx *actx, short i, struct AttachPtrArray *aa)
142{
143 short level = actx->idx[i]->level;
144 short count = 0;
145
146 while ((++i < actx->idxlen) && (level < actx->idx[i]->level))
147 if (aa_contains_body(aa, actx->idx[i]->body))
148 count++;
149
150 return count;
151}
152
158void attach_bounce_message(struct AttachPtrArray *aa, struct Mailbox *m)
159{
160 if (!m || ARRAY_EMPTY(aa))
161 return;
162
163 if (!check_all_msg(aa, true))
164 return;
165
166 struct AddressList al = TAILQ_HEAD_INITIALIZER(al);
167 struct Buffer *prompt = buf_pool_get();
168 struct Buffer *buf = buf_pool_get();
169
170 /* RFC5322 mandates a From: header, so warn before bouncing
171 * messages without one */
172 struct AttachPtr **app = NULL;
173 ARRAY_FOREACH(app, aa)
174 {
175 if (TAILQ_EMPTY(&(*app)->body->email->env->from))
176 {
177 mutt_error(_("Warning: message contains no From: header"));
179 break;
180 }
181 }
182
183 /* one or more messages? */
184 const int num_msg = ARRAY_SIZE(aa);
185 if (num_msg == 1)
186 buf_strcpy(prompt, _("Bounce message to: "));
187 else
188 buf_strcpy(prompt, _("Bounce messages to: "));
189
191 &CompleteAliasOps, NULL) != 0) ||
192 buf_is_empty(buf))
193 {
194 goto done;
195 }
196
198 if (TAILQ_EMPTY(&al))
199 {
200 mutt_error(_("Error parsing address"));
201 goto done;
202 }
203
205
206 char *err = NULL;
207 if (mutt_addrlist_to_intl(&al, &err) < 0)
208 {
209 mutt_error(_("Bad IDN: '%s'"), err);
210 FREE(&err);
211 goto done;
212 }
213
214 buf_reset(buf);
215 buf_alloc(buf, 8192);
216 mutt_addrlist_write(&al, buf, true);
217
218 buf_printf(prompt, ngettext("Bounce message to %s?", "Bounce messages to %s?", num_msg),
219 buf_string(buf));
220
221 if (query_quadoption(buf_string(prompt), NeoMutt->sub, "bounce") != MUTT_YES)
222 {
223 msgwin_clear_text(NULL);
224 mutt_message(ngettext("Message not bounced", "Messages not bounced", num_msg));
225 goto done;
226 }
227
228 msgwin_clear_text(NULL);
229
230 int rc = 0;
231 ARRAY_FOREACH(app, aa)
232 {
233 if (mutt_bounce_message((*app)->fp, m, (*app)->body->email, &al, NeoMutt->sub))
234 {
235 rc = 1;
236 }
237 }
238
239 if (rc == 0)
240 mutt_message(ngettext("Message bounced", "Messages bounced", num_msg));
241 else
242 mutt_error(ngettext("Error bouncing message", "Error bouncing messages", num_msg));
243
244done:
246 buf_pool_release(&buf);
247 buf_pool_release(&prompt);
248}
249
255void mutt_attach_resend(struct AttachPtrArray *aa, struct Mailbox *m)
256{
257 if (!check_all_msg(aa, true))
258 return;
259
260 struct AttachPtr **app = NULL;
261 ARRAY_FOREACH(app, aa)
262 {
263 mutt_resend_message((*app)->fp, m, (*app)->body->email, NeoMutt->sub);
264 }
265}
266
274static struct AttachPtr *find_common_parent(struct AttachCtx *actx, struct AttachPtrArray *aa)
275{
276 short i;
277 const short nattach = ARRAY_SIZE(aa);
278
279 for (i = 0; i < actx->idxlen; i++)
280 if (aa_contains_body(aa, actx->idx[i]->body))
281 break;
282
283 while (--i >= 0)
284 {
285 if (mutt_is_message_type(actx->idx[i]->body->type, actx->idx[i]->body->subtype))
286 {
287 const short nchildren = count_selected_children(actx, i, aa);
288 if (nchildren == nattach)
289 return actx->idx[i];
290 }
291 }
292
293 return NULL;
294}
295
308static int is_parent(short i, struct AttachCtx *actx, const struct Body *b)
309{
310 short level = actx->idx[i]->level;
311
312 while ((++i < actx->idxlen) && (actx->idx[i]->level > level))
313 {
314 if (actx->idx[i]->body == b)
315 return true;
316 }
317
318 return false;
319}
320
328static struct AttachPtr *find_parent(struct AttachCtx *actx, struct AttachPtrArray *aa)
329{
330 struct AttachPtr *parent = NULL;
331 const short nattach = ARRAY_SIZE(aa);
332
333 if (nattach == 1)
334 {
335 struct AttachPtr **app = ARRAY_GET(aa, 0);
336 struct Body *b = app ? (*app)->body : NULL;
337 for (short i = 0; i < actx->idxlen; i++)
338 {
339 if (mutt_is_message_type(actx->idx[i]->body->type, actx->idx[i]->body->subtype) &&
340 is_parent(i, actx, b))
341 {
342 parent = actx->idx[i];
343 }
344 if (actx->idx[i]->body == b)
345 break;
346 }
347 }
348 else if (nattach)
349 {
350 parent = find_common_parent(actx, aa);
351 }
352
353 return parent;
354}
355
364static void include_header(bool quote, FILE *fp_in, struct Email *e,
365 FILE *fp_out, const char *prefix)
366{
367 CopyHeaderFlags chflags = CH_DECODE;
368 struct Buffer *prefix2 = buf_pool_get();
369
370 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
371 if (c_weed)
372 chflags |= CH_WEED | CH_REORDER;
373
374 if (quote)
375 {
376 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
377 if (prefix)
378 {
379 buf_strcpy(prefix2, prefix);
380 }
381 else if (!c_text_flowed)
382 {
383 const char *const c_attribution_locale = cs_subset_string(NeoMutt->sub, "attribution_locale");
384 const struct Expando *c_indent_string = cs_subset_expando(NeoMutt->sub, "indent_string");
385 setlocale(LC_TIME, NONULL(c_attribution_locale));
386 mutt_make_string(prefix2, -1, c_indent_string, NULL, -1, e, MUTT_FORMAT_NONE, NULL);
387 setlocale(LC_TIME, "");
388 }
389 else
390 {
391 buf_strcpy(prefix2, ">");
392 }
393
394 chflags |= CH_PREFIX;
395 }
396
397 mutt_copy_header(fp_in, e, fp_out, chflags, quote ? buf_string(prefix2) : NULL, 0);
398 buf_pool_release(&prefix2);
399}
400
410static struct Body **copy_problematic_attachments(struct Body **last,
411 struct AttachPtrArray *aa, bool force)
412{
413 struct AttachPtr **app = NULL;
414 ARRAY_FOREACH(app, aa)
415 {
416 if (force || !mutt_can_decode((*app)->body))
417 {
418 if (mutt_body_copy((*app)->fp, last, (*app)->body) == -1)
419 return NULL; /* XXXXX - may lead to crashes */
420 last = &((*last)->next);
421 }
422 }
423 return last;
424}
425
434static void attach_forward_bodies(struct Email *e, struct AttachCtx *actx,
435 struct AttachPtrArray *aa)
436{
437 bool mime_fwd_all = false;
438 bool mime_fwd_any = true;
439 struct Email *e_parent = NULL;
440 FILE *fp_parent = NULL;
441 enum QuadOption ans = MUTT_NO;
442 struct Buffer *tempfile = NULL;
443 struct Buffer *prefix = buf_pool_get();
444 const short nattach = ARRAY_SIZE(aa);
445 struct AttachPtr **first = ARRAY_GET(aa, 0);
446 struct AttachPtr *current = first ? *first : NULL;
447 struct Body *b = current ? current->body : NULL;
448 FILE *fp = current ? current->fp : NULL;
449
450 /* First, find the parent message.
451 * Note: This could be made an option by just
452 * putting the following lines into an if block. */
453 struct AttachPtr *parent = find_parent(actx, aa);
454 if (parent)
455 {
456 e_parent = parent->body->email;
457 fp_parent = parent->fp;
458 }
459 else
460 {
461 e_parent = e;
462 fp_parent = actx->fp_root;
463 }
464
465 struct Email *e_tmp = email_new();
466 e_tmp->env = mutt_env_new();
467 mutt_make_forward_subject(e_tmp->env, e_parent, NeoMutt->sub);
468
469 tempfile = buf_pool_get();
470 buf_mktemp_draft(tempfile);
471 FILE *fp_tmp = mutt_file_fopen(buf_string(tempfile), "w");
472 if (!fp_tmp)
473 {
474 mutt_error(_("Can't open temporary file %s"), buf_string(tempfile));
475 email_free(&e_tmp);
476 goto bail;
477 }
478
479 mutt_forward_intro(e_parent, fp_tmp, NeoMutt->sub);
480
481 /* prepare the prefix here since we'll need it later. */
482
483 const bool c_forward_quote = cs_subset_bool(NeoMutt->sub, "forward_quote");
484 if (c_forward_quote)
485 {
486 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
487 if (c_text_flowed)
488 {
489 buf_strcpy(prefix, ">");
490 }
491 else
492 {
493 const char *const c_attribution_locale = cs_subset_string(NeoMutt->sub, "attribution_locale");
494 const struct Expando *c_indent_string = cs_subset_expando(NeoMutt->sub, "indent_string");
495 setlocale(LC_TIME, NONULL(c_attribution_locale));
496 mutt_make_string(prefix, -1, c_indent_string, NULL, -1, e_parent,
497 MUTT_FORMAT_NONE, NULL);
498 setlocale(LC_TIME, "");
499 }
500 }
501
502 include_header(c_forward_quote, fp_parent, e_parent, fp_tmp, buf_string(prefix));
503
504 /* Now, we have prepared the first part of the message body: The
505 * original message's header.
506 *
507 * The next part is more interesting: either include the message bodies,
508 * or attach them. */
509 if ((!b || mutt_can_decode(b)) &&
510 ((ans = query_quadoption(_("Forward as attachments?"), NeoMutt->sub, "mime_forward")) == MUTT_YES))
511 {
512 mime_fwd_all = true;
513 }
514 else if (ans == MUTT_ABORT)
515 {
516 goto bail;
517 }
518
519 /* shortcut MIMEFWDREST when there is only one attachment.
520 * Is this intuitive? */
521 if (!mime_fwd_all && (nattach > 1) && !check_can_decode(aa))
522 {
523 ans = query_quadoption(_("Can't decode all selected attachments. MIME-forward the others?"),
524 NeoMutt->sub, "mime_forward_rest");
525 if (ans == MUTT_ABORT)
526 goto bail;
527 else if (ans == MUTT_NO)
528 mime_fwd_any = false;
529 }
530
531 /* initialize a state structure */
532
533 struct State state = { 0 };
534 if (c_forward_quote)
535 state.prefix = buf_string(prefix);
536 state.flags = STATE_CHARCONV;
537 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
538 if (c_weed)
539 state.flags |= STATE_WEED;
540 state.fp_out = fp_tmp;
541
542 /* where do we append new MIME parts? */
543 struct Body **last = &e_tmp->body;
544
545 if (nattach == 1)
546 {
547 /* single body case */
548
549 if (!mime_fwd_all && mutt_can_decode(b))
550 {
551 state.fp_in = fp;
552 mutt_body_handler(b, &state);
553 state_putc(&state, '\n');
554 }
555 else
556 {
557 if (mutt_body_copy(fp, last, b) == -1)
558 goto bail;
559 }
560 }
561 else
562 {
563 /* multiple body case */
564
565 if (!mime_fwd_all)
566 {
567 struct AttachPtr **app = NULL;
568 ARRAY_FOREACH(app, aa)
569 {
570 if (mutt_can_decode((*app)->body))
571 {
572 state.fp_in = (*app)->fp;
573 mutt_body_handler((*app)->body, &state);
574 state_putc(&state, '\n');
575 }
576 }
577 }
578
579 if (mime_fwd_any && !copy_problematic_attachments(last, aa, mime_fwd_all))
580 goto bail;
581 }
582
583 mutt_forward_trailer(e_parent, fp_tmp, NeoMutt->sub);
584
585 mutt_file_fclose(&fp_tmp);
586 fp_tmp = NULL;
587
588 /* now that we have the template, send it. */
589 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
590 ARRAY_ADD(&ea, e_parent);
591 mutt_send_message(SEND_NONE, e_tmp, buf_string(tempfile), NULL, &ea, NeoMutt->sub);
592 ARRAY_FREE(&ea);
593 buf_pool_release(&tempfile);
594 buf_pool_release(&prefix);
595 return;
596
597bail:
598 if (fp_tmp)
599 {
600 mutt_file_fclose(&fp_tmp);
601 mutt_file_unlink(buf_string(tempfile));
602 }
603 buf_pool_release(&tempfile);
604 buf_pool_release(&prefix);
605
606 email_free(&e_tmp);
607}
608
621static void attach_forward_msgs(struct AttachPtrArray *aa, SendFlags flags)
622{
623 struct AttachPtr **first = ARRAY_GET(aa, 0);
624 if (!first)
625 return;
626
627 struct AttachPtr *current = *first;
628 FILE *fp = current->fp;
629 struct Body *b = current->body;
630 struct Email *e_cur = current->body->email;
631 struct Email *e_tmp = NULL;
632 enum QuadOption ans;
633 struct Body **last = NULL;
634 struct Buffer *tempfile = NULL;
635 FILE *fp_tmp = NULL;
636
637 CopyHeaderFlags chflags = CH_DECODE;
638
639 e_tmp = email_new();
640 e_tmp->env = mutt_env_new();
641 mutt_make_forward_subject(e_tmp->env, e_cur, NeoMutt->sub);
642
643 tempfile = buf_pool_get();
644
645 ans = query_quadoption(_("Forward MIME encapsulated?"), NeoMutt->sub, "mime_forward");
646 if (ans == MUTT_NO)
647 {
648 /* no MIME encapsulation */
649
650 buf_mktemp_draft(tempfile);
651 fp_tmp = mutt_file_fopen(buf_string(tempfile), "w");
652 if (!fp_tmp)
653 {
654 mutt_error(_("Can't create %s"), buf_string(tempfile));
655 goto cleanup;
656 }
657
659 const bool c_forward_quote = cs_subset_bool(NeoMutt->sub, "forward_quote");
660 if (c_forward_quote)
661 {
662 chflags |= CH_PREFIX;
663 cmflags |= MUTT_CM_PREFIX;
664 }
665
666 const bool c_forward_decode = cs_subset_bool(NeoMutt->sub, "forward_decode");
667 if (c_forward_decode)
668 {
669 cmflags |= MUTT_CM_DECODE | MUTT_CM_CHARCONV;
670 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
671 if (c_weed)
672 {
673 chflags |= CH_WEED | CH_REORDER;
674 cmflags |= MUTT_CM_WEED;
675 }
676 }
677
678 if (ARRAY_SIZE(aa) == 1)
679 {
680 mutt_forward_intro(b->email, fp_tmp, NeoMutt->sub);
681 mutt_copy_message_fp(fp_tmp, fp, b->email, cmflags, chflags, 0);
683 }
684 else
685 {
686 struct AttachPtr **app = NULL;
687 ARRAY_FOREACH(app, aa)
688 {
689 mutt_forward_intro((*app)->body->email, fp_tmp, NeoMutt->sub);
690 mutt_copy_message_fp(fp_tmp, (*app)->fp, (*app)->body->email, cmflags, chflags, 0);
691 mutt_forward_trailer((*app)->body->email, fp_tmp, NeoMutt->sub);
692 }
693 }
694 mutt_file_fclose(&fp_tmp);
695 }
696 else if (ans == MUTT_YES) /* do MIME encapsulation - we don't need to do much here */
697 {
698 last = &e_tmp->body;
699 if (ARRAY_SIZE(aa) == 1)
700 {
701 mutt_body_copy(fp, last, b);
702 }
703 else
704 {
705 struct AttachPtr **app = NULL;
706 ARRAY_FOREACH(app, aa)
707 {
708 mutt_body_copy((*app)->fp, last, (*app)->body);
709 last = &((*last)->next);
710 }
711 }
712 }
713 else
714 {
715 email_free(&e_tmp);
716 }
717
718 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
719 ARRAY_ADD(&ea, e_cur);
720 mutt_send_message(flags, e_tmp, buf_is_empty(tempfile) ? NULL : buf_string(tempfile),
721 NULL, &ea, NeoMutt->sub);
722 ARRAY_FREE(&ea);
723 e_tmp = NULL; /* mutt_send_message frees this */
724
725cleanup:
726 email_free(&e_tmp);
727 buf_pool_release(&tempfile);
728}
729
737void mutt_attach_forward(struct AttachPtrArray *aa, struct Email *e,
738 struct AttachCtx *actx, SendFlags flags)
739{
740 if (check_all_msg(aa, false))
741 {
742 attach_forward_msgs(aa, flags);
743 }
744 else
745 {
746 attach_forward_bodies(e, actx, aa);
747 }
748}
749
769static int attach_reply_envelope_defaults(struct Envelope *env, struct AttachPtrArray *aa,
770 struct Email *parent, SendFlags flags)
771{
772 struct Envelope *curenv = NULL;
773 struct Email *e = NULL;
774
775 if (parent)
776 {
777 curenv = parent->env;
778 e = parent;
779 }
780 else
781 {
782 struct AttachPtr **app = NULL;
783 ARRAY_FOREACH(app, aa)
784 {
785 e = (*app)->body->email;
786 curenv = e->env;
787 break;
788 }
789 }
790
791 if (!curenv || !e)
792 {
793 mutt_error(_("Can't find any selected messages"));
794 return -1;
795 }
796
797 if ((flags & SEND_NEWS))
798 {
799 /* in case followup set Newsgroups: with Followup-To: if it present */
800 if (!env->newsgroups && curenv && !mutt_istr_equal(curenv->followup_to, "poster"))
801 {
802 env->newsgroups = mutt_str_dup(curenv->followup_to);
803 }
804 }
805 else
806 {
807 if (parent)
808 {
809 if (mutt_fetch_recips(env, curenv, flags, NeoMutt->sub) == -1)
810 return -1;
811 }
812 else
813 {
814 struct AttachPtr **app = NULL;
815 ARRAY_FOREACH(app, aa)
816 {
817 if (mutt_fetch_recips(env, (*app)->body->email->env, flags, NeoMutt->sub) == -1)
818 {
819 return -1;
820 }
821 }
822 }
823
824 if ((flags & SEND_LIST_REPLY) && TAILQ_EMPTY(&env->to))
825 {
826 mutt_error(_("No mailing lists found"));
827 return -1;
828 }
829
831 }
833
834 if (parent)
835 {
837 }
838 else
839 {
840 struct AttachPtr **app = NULL;
841 ARRAY_FOREACH(app, aa)
842 {
843 mutt_add_to_reference_headers(env, (*app)->body->email->env, NeoMutt->sub);
844 }
845 }
846
847 return 0;
848}
849
856static void attach_include_reply(FILE *fp, FILE *fp_tmp, struct Email *e)
857{
859 CopyHeaderFlags chflags = CH_DECODE;
860
862
863 const bool c_header = cs_subset_bool(NeoMutt->sub, "header");
864 if (!c_header)
865 cmflags |= MUTT_CM_NOHEADER;
866 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
867 if (c_weed)
868 {
869 chflags |= CH_WEED;
870 cmflags |= MUTT_CM_WEED;
871 }
872
873 mutt_copy_message_fp(fp_tmp, fp, e, cmflags, chflags, 0);
875}
876
885void mutt_attach_reply(struct AttachPtrArray *aa, struct Mailbox *m,
886 struct Email *e, struct AttachCtx *actx, SendFlags flags)
887{
888 bool mime_reply_any = false;
889
890 const short nattach = ARRAY_SIZE(aa);
891 struct AttachPtr **first = ARRAY_GET(aa, 0);
892 if (!first)
893 return;
894
895 struct AttachPtr *current = *first;
896 FILE *fp = current->fp;
897 struct Body *b = current->body;
898 struct AttachPtr *parent = NULL;
899 struct Email *e_parent = NULL;
900 FILE *fp_parent = NULL;
901 struct Email *e_tmp = NULL;
902 FILE *fp_tmp = NULL;
903 struct Buffer *tempfile = NULL;
904 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
905
906 struct Buffer *prefix = buf_pool_get();
907
908 if (flags & SEND_NEWS)
909 OptNewsSend = true;
910 else
911 OptNewsSend = false;
912
913 /* Determine the parent message for the reply: either the selected
914 * attachment's parent or the top-level email if not nested */
915 if (!check_all_msg(aa, false))
916 {
917 parent = find_parent(actx, aa);
918 if (parent)
919 {
920 e_parent = parent->body->email;
921 fp_parent = parent->fp;
922 }
923 else
924 {
925 e_parent = e;
926 fp_parent = actx->fp_root;
927 }
928 }
929
930 /* Check if non-decodable attachments should be MIME-encapsulated */
931 if ((nattach > 1) && !check_can_decode(aa))
932 {
933 const enum QuadOption ans = query_quadoption(_("Can't decode all selected attachments. MIME-encapsulate the others?"),
934 NeoMutt->sub, "mime_forward_rest");
935 if (ans == MUTT_ABORT)
936 goto cleanup;
937 if (ans == MUTT_YES)
938 mime_reply_any = true;
939 }
940 else if (nattach == 1)
941 {
942 mime_reply_any = true;
943 }
944
945 e_tmp = email_new();
946 e_tmp->env = mutt_env_new();
947
948 if (attach_reply_envelope_defaults(e_tmp->env, aa,
949 e_parent ? e_parent : (b ? b->email : NULL),
950 flags) == -1)
951 {
952 goto cleanup;
953 }
954
955 /* Create a temporary file for the reply body and write the quoted
956 * content of the selected attachment(s) into it */
957 tempfile = buf_pool_get();
958 buf_mktemp_draft(tempfile);
959 fp_tmp = mutt_file_fopen(buf_string(tempfile), "w");
960 if (!fp_tmp)
961 {
962 mutt_error(_("Can't create %s"), buf_string(tempfile));
963 goto cleanup;
964 }
965
966 if (e_parent)
967 {
968 mutt_make_attribution_intro(e_parent, fp_tmp, NeoMutt->sub);
969
970 struct State state = { 0 };
971 state.fp_out = fp_tmp;
972
973 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
974 if (c_text_flowed)
975 {
976 buf_strcpy(prefix, ">");
977 }
978 else
979 {
980 const char *const c_attribution_locale = cs_subset_string(NeoMutt->sub, "attribution_locale");
981 const struct Expando *c_indent_string = cs_subset_expando(NeoMutt->sub, "indent_string");
982 setlocale(LC_TIME, NONULL(c_attribution_locale));
983 mutt_make_string(prefix, -1, c_indent_string, m, -1, e_parent, MUTT_FORMAT_NONE, NULL);
984 setlocale(LC_TIME, "");
985 }
986
987 state.prefix = buf_string(prefix);
988 state.flags = STATE_CHARCONV;
989
990 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
991 if (c_weed)
992 state.flags |= STATE_WEED;
993
994 const bool c_header = cs_subset_bool(NeoMutt->sub, "header");
995 if (c_header)
996 include_header(true, fp_parent, e_parent, fp_tmp, buf_string(prefix));
997
998 if (nattach == 1)
999 {
1000 if (mutt_can_decode(b))
1001 {
1002 state.fp_in = fp;
1003 mutt_body_handler(b, &state);
1004 state_putc(&state, '\n');
1005 }
1006 else
1007 {
1008 mutt_body_copy(fp, &e_tmp->body, b);
1009 }
1010 }
1011 else
1012 {
1013 struct AttachPtr **app = NULL;
1014 ARRAY_FOREACH(app, aa)
1015 {
1016 if (mutt_can_decode((*app)->body))
1017 {
1018 state.fp_in = (*app)->fp;
1019 mutt_body_handler((*app)->body, &state);
1020 state_putc(&state, '\n');
1021 }
1022 }
1023 }
1024
1025 mutt_make_attribution_trailer(e_parent, fp_tmp, NeoMutt->sub);
1026
1027 if (mime_reply_any && (nattach > 1) &&
1028 !copy_problematic_attachments(&e_tmp->body, aa, false))
1029 {
1030 goto cleanup;
1031 }
1032 }
1033 else
1034 {
1035 if (nattach == 1)
1036 {
1037 attach_include_reply(fp, fp_tmp, b->email);
1038 }
1039 else
1040 {
1041 struct AttachPtr **app = NULL;
1042 ARRAY_FOREACH(app, aa)
1043 {
1044 attach_include_reply((*app)->fp, fp_tmp, (*app)->body->email);
1045 }
1046 }
1047 }
1048
1049 mutt_file_fclose(&fp_tmp);
1050
1051 ARRAY_ADD(&ea, e_parent ? e_parent : b->email);
1052 if (mutt_send_message(flags, e_tmp, buf_string(tempfile), NULL, &ea, NeoMutt->sub) == 0)
1053 {
1054 mutt_set_flag(m, e, MUTT_REPLIED, true, true);
1055 }
1056 e_tmp = NULL; /* mutt_send_message frees this */
1057
1058cleanup:
1059 if (fp_tmp)
1060 {
1061 mutt_file_fclose(&fp_tmp);
1062 mutt_file_unlink(buf_string(tempfile));
1063 }
1064 buf_pool_release(&tempfile);
1065 buf_pool_release(&prefix);
1066 email_free(&e_tmp);
1067 ARRAY_FREE(&ea);
1068}
1069
1074void mutt_attach_mail_sender(struct AttachPtrArray *aa)
1075{
1076 if (!check_all_msg(aa, false))
1077 {
1078 /* L10N: You will see this error message if you invoke <compose-to-sender>
1079 when you are on a normal attachment. */
1080 mutt_error(_("You may only compose to sender with message/rfc822 parts"));
1081 return;
1082 }
1083
1084 struct Email *e_tmp = email_new();
1085 e_tmp->env = mutt_env_new();
1086
1087 struct AttachPtr **app = NULL;
1088 ARRAY_FOREACH(app, aa)
1089 {
1090 if (mutt_fetch_recips(e_tmp->env, (*app)->body->email->env, SEND_TO_SENDER,
1091 NeoMutt->sub) == -1)
1092 {
1093 email_free(&e_tmp);
1094 return;
1095 }
1096 }
1097
1098 // This call will free e_tmp for us
1099 mutt_send_message(SEND_NONE, e_tmp, NULL, NULL, NULL, NeoMutt->sub);
1100}
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition address.c:1469
size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
Write an Address to a buffer.
Definition address.c:1215
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition address.c:480
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition address.c:1302
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:296
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:157
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
#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:209
#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
Handling of email attachments.
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition buffer.c:337
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
const struct Expando * cs_subset_expando(const struct ConfigSubset *sub, const char *name)
Get an Expando config item by name.
Convenience wrapper for the config headers.
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 copy_body.c:50
int mutt_copy_header(FILE *fp_in, struct Email *e, FILE *fp_out, CopyHeaderFlags chflags, const char *prefix, int wraplen)
Copy Email header.
Definition copy_email.c:432
int mutt_copy_message_fp(FILE *fp_out, FILE *fp_in, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
Make a copy of a message from a FILE pointer.
Definition copy_email.c:663
@ MUTT_CM_PREFIX
Quote the header and body.
Definition copy_email.h:43
@ MUTT_CM_DECODE
Decode the message body into text/plain.
Definition copy_email.h:44
@ MUTT_CM_NOHEADER
Don't copy the message header.
Definition copy_email.h:42
@ MUTT_CM_CHARCONV
Perform character set conversions.
Definition copy_email.h:48
@ MUTT_CM_WEED
Weed message/rfc822 attachment headers.
Definition copy_email.h:47
@ MUTT_CM_NONE
No flags are set.
Definition copy_email.h:41
uint32_t CopyHeaderFlags
Definition copy_email.h:89
@ CH_WEED
Weed the headers?
Definition copy_email.h:67
@ CH_PREFIX
Quote header using $indent_string string?
Definition copy_email.h:71
@ CH_DECODE
Do RFC2047 header decoding.
Definition copy_email.h:68
@ CH_REORDER
Re-order output of headers (specified by 'header-order')
Definition copy_email.h:73
uint16_t CopyMessageFlags
Definition copy_email.h:55
Convenience wrapper for the core headers.
int mutt_make_string(struct Buffer *buf, size_t max_cols, const struct Expando *exp, struct Mailbox *m, int inpgr, struct Email *e, MuttFormatFlags flags, const char *progress)
Create formatted strings using mailbox expandos.
Definition dlg_index.c:824
Edit a string.
@ MUTT_COMP_NONE
No flags are set.
Definition wdata.h:46
struct Email * email_new(void)
Create a new Email.
Definition email.c:77
void email_free(struct Email **ptr)
Free an Email.
Definition email.c:46
Structs that make up an email.
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition parse.c:1470
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition envelope.c:45
Parse Expando string.
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition file.c:156
#define mutt_file_fclose(FP)
Definition file.h:144
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
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:54
bool OptNewsSend
(pseudo) used to change behavior when posting
Definition globals.c:54
Global variables.
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:467
#define mutt_error(...)
Definition logging2.h:94
#define mutt_message(...)
Definition logging2.h:93
Convenience wrapper for the gui headers.
bool mutt_can_decode(struct Body *b)
Will decoding the attachment produce any output.
Definition handler.c:1898
int mutt_body_handler(struct Body *b, struct State *state)
Handler for the Body of an email.
Definition handler.c:1664
Read/write command history from/to a file.
@ HC_ALIAS
Aliases.
Definition lib.h:57
GUI manage the main index (list of emails)
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
void msgwin_clear_text(struct MuttWindow *win)
Clear the text in the Message Window.
Definition msgwin.c:527
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
#define state_putc(STATE, STR)
Definition state.h:65
@ STATE_CHARCONV
Do character set conversions.
Definition state.h:41
@ STATE_WEED
Weed headers even when not in display mode.
Definition state.h:40
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:677
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
Many unsorted constants and some structs.
@ MUTT_REPLIED
Messages that have been replied to.
Definition mutt.h:91
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
NeoMutt Logging.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
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_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:384
#define TAILQ_HEAD_INITIALIZER(head)
Definition queue.h:694
#define TAILQ_EMPTY(head)
Definition queue.h:778
static void attach_include_reply(FILE *fp, FILE *fp_tmp, struct Email *e)
This is very similar to send.c's include_reply()
Definition recvcmd.c:856
void mutt_attach_mail_sender(struct AttachPtrArray *aa)
Compose an email to the sender in the email attachment.
Definition recvcmd.c:1074
static void attach_forward_bodies(struct Email *e, struct AttachCtx *actx, struct AttachPtrArray *aa)
Forward one or several MIME bodies.
Definition recvcmd.c:434
static bool check_all_msg(struct AttachPtrArray *aa, bool err)
Are all the selected Attachments RFC822 messages?
Definition recvcmd.c:85
static short count_selected_children(struct AttachCtx *actx, short i, struct AttachPtrArray *aa)
Selected children below a multipart/message attachment.
Definition recvcmd.c:141
static struct AttachPtr * find_common_parent(struct AttachCtx *actx, struct AttachPtrArray *aa)
Find a common parent message for the selected attachments.
Definition recvcmd.c:274
static bool aa_contains_body(struct AttachPtrArray *aa, const struct Body *b)
Does the selection contain a Body?
Definition recvcmd.c:119
static void attach_forward_msgs(struct AttachPtrArray *aa, SendFlags flags)
Forward one or several message-type attachments.
Definition recvcmd.c:621
static struct AttachPtr * find_parent(struct AttachCtx *actx, struct AttachPtrArray *aa)
Find the parent of a selected Attachment set.
Definition recvcmd.c:328
static bool check_can_decode(struct AttachPtrArray *aa)
Can we decode all selected attachments?
Definition recvcmd.c:101
static int attach_reply_envelope_defaults(struct Envelope *env, struct AttachPtrArray *aa, struct Email *parent, SendFlags flags)
Create the envelope defaults for a reply.
Definition recvcmd.c:769
static void include_header(bool quote, FILE *fp_in, struct Email *e, FILE *fp_out, const char *prefix)
Write an email header to a file, optionally quoting it.
Definition recvcmd.c:364
static int is_parent(short i, struct AttachCtx *actx, const struct Body *b)
Check whether one attachment is the parent of another.
Definition recvcmd.c:308
void mutt_attach_reply(struct AttachPtrArray *aa, struct Mailbox *m, struct Email *e, struct AttachCtx *actx, SendFlags flags)
Attach a reply.
Definition recvcmd.c:885
void mutt_attach_resend(struct AttachPtrArray *aa, struct Mailbox *m)
Resend-message, from the attachment menu.
Definition recvcmd.c:255
void attach_bounce_message(struct AttachPtrArray *aa, struct Mailbox *m)
Bounce function, from the attachment menu.
Definition recvcmd.c:158
void mutt_attach_forward(struct AttachPtrArray *aa, struct Email *e, struct AttachCtx *actx, SendFlags flags)
Forward selected attachments.
Definition recvcmd.c:737
static bool check_msg(struct Body *b, bool err)
Are we working with an RFC822 message.
Definition recvcmd.c:68
static struct Body ** copy_problematic_attachments(struct Body **last, struct AttachPtrArray *aa, bool force)
Attach the body parts which can't be decoded.
Definition recvcmd.c:410
Send/reply with an attachment.
@ MUTT_FORMAT_NONE
No flags are set.
Definition render.h:37
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:1011
void mutt_make_attribution_intro(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add "on DATE, PERSON wrote" header.
Definition send.c:668
void mutt_forward_intro(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add the "start of forwarded message" text.
Definition send.c:461
void mutt_make_forward_subject(struct Envelope *env, struct Email *e, struct ConfigSubset *sub)
Create a subject for a forwarded email.
Definition send.c:991
void mutt_fix_reply_recipients(struct Envelope *env, struct ConfigSubset *sub)
Remove duplicate recipients.
Definition send.c:961
int mutt_resend_message(FILE *fp, struct Mailbox *m, struct Email *e_cur, struct ConfigSubset *sub)
Resend an email.
Definition send.c:1560
int mutt_fetch_recips(struct Envelope *out, struct Envelope *in, SendFlags flags, struct ConfigSubset *sub)
Generate recpients for a reply email.
Definition send.c:878
void mutt_forward_trailer(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add a "end of forwarded message" text.
Definition send.c:484
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:2030
void mutt_make_attribution_trailer(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add suffix to replied email text.
Definition send.c:679
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:1039
uint32_t SendFlags
Definition send.h:64
@ SEND_NONE
No flags are set.
Definition send.h:45
@ SEND_TO_SENDER
Compose new email to sender.
Definition send.h:57
@ SEND_LIST_REPLY
Reply to mailing list.
Definition send.h:48
@ SEND_NEWS
Reply to a news article.
Definition send.h:59
int mutt_bounce_message(FILE *fp, struct Mailbox *m, struct Email *e, struct AddressList *to, struct ConfigSubset *sub)
Bounce an email message.
Definition sendlib.c:883
#define NONULL(x)
Definition string2.h:44
A set of attachments.
Definition attach.h:65
FILE * fp_root
Used by recvattach for updating.
Definition attach.h:67
struct AttachPtr ** idx
Array of attachments.
Definition attach.h:69
short idxlen
Number of attachmentes.
Definition attach.h:70
An email to which things will be attached.
Definition attach.h:36
struct Body * body
Attachment.
Definition attach.h:37
int level
Nesting depth of attachment.
Definition attach.h:41
FILE * fp
Used in the recvattach menu.
Definition attach.h:38
The body of an email.
Definition body.h:36
struct Email * email
header information for message/rfc822
Definition body.h:74
char * subtype
content-type subtype
Definition body.h:61
unsigned int type
content-type primary type, ContentType
Definition body.h:40
String manipulation buffer.
Definition buffer.h:36
The envelope/body of an email.
Definition email.h:39
struct Envelope * env
Envelope information.
Definition email.h:68
struct Body * body
List of MIME parts.
Definition email.h:69
The header of an Email.
Definition envelope.h:57
struct AddressList to
Email's 'To' list.
Definition envelope.h:60
char * followup_to
List of 'followup-to' fields.
Definition envelope.h:80
char * newsgroups
List of newsgroups.
Definition envelope.h:78
Parsed Expando trees.
Definition expando.h:41
A mailbox.
Definition mailbox.h:81
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
Keep track when processing files.
Definition state.h:54
StateFlags flags
Flags, e.g. STATE_DISPLAY.
Definition state.h:58
FILE * fp_out
File to write to.
Definition state.h:56
FILE * fp_in
File to read from.
Definition state.h:55
const char * prefix
String to add to the beginning of each output line.
Definition state.h:57
#define buf_mktemp_draft(buf)
Definition tmp.h:34