NeoMutt  2025-12-11-435-g4ac674
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
86static bool check_all_msg(struct AttachCtx *actx, struct Body *b, bool err)
87{
88 if (b && !check_msg(b, err))
89 return false;
90 if (!b)
91 {
92 for (short i = 0; i < actx->idxlen; i++)
93 {
94 if (actx->idx[i]->body->tagged)
95 {
96 if (!check_msg(actx->idx[i]->body, err))
97 return false;
98 }
99 }
100 }
101 return true;
102}
103
110static bool check_can_decode(struct AttachCtx *actx, struct Body *b)
111{
112 if (b)
113 return mutt_can_decode(b);
114
115 for (short i = 0; i < actx->idxlen; i++)
116 if (actx->idx[i]->body->tagged && !mutt_can_decode(actx->idx[i]->body))
117 return false;
118
119 return true;
120}
121
127static short count_tagged(struct AttachCtx *actx)
128{
129 short count = 0;
130 for (short i = 0; i < actx->idxlen; i++)
131 if (actx->idx[i]->body->tagged)
132 count++;
133
134 return count;
135}
136
143static short count_tagged_children(struct AttachCtx *actx, short i)
144{
145 short level = actx->idx[i]->level;
146 short count = 0;
147
148 while ((++i < actx->idxlen) && (level < actx->idx[i]->level))
149 if (actx->idx[i]->body->tagged)
150 count++;
151
152 return count;
153}
154
162void attach_bounce_message(struct Mailbox *m, FILE *fp, struct AttachCtx *actx,
163 struct Body *b)
164{
165 if (!m || !fp || !actx)
166 return;
167
168 if (!check_all_msg(actx, b, true))
169 return;
170
171 struct AddressList al = TAILQ_HEAD_INITIALIZER(al);
172 struct Buffer *prompt = buf_pool_get();
173 struct Buffer *buf = buf_pool_get();
174
175 /* RFC5322 mandates a From: header, so warn before bouncing
176 * messages without one */
177 if (b)
178 {
179 if (TAILQ_EMPTY(&b->email->env->from))
180 {
181 mutt_error(_("Warning: message contains no From: header"));
183 }
184 }
185 else
186 {
187 for (short i = 0; i < actx->idxlen; i++)
188 {
189 if (actx->idx[i]->body->tagged)
190 {
191 if (TAILQ_EMPTY(&actx->idx[i]->body->email->env->from))
192 {
193 mutt_error(_("Warning: message contains no From: header"));
195 break;
196 }
197 }
198 }
199 }
200
201 /* one or more messages? */
202 int num_msg = b ? 1 : count_tagged(actx);
203 if (num_msg == 1)
204 buf_strcpy(prompt, _("Bounce message to: "));
205 else
206 buf_strcpy(prompt, _("Bounce tagged messages to: "));
207
209 &CompleteAliasOps, NULL) != 0) ||
210 buf_is_empty(buf))
211 {
212 goto done;
213 }
214
216 if (TAILQ_EMPTY(&al))
217 {
218 mutt_error(_("Error parsing address"));
219 goto done;
220 }
221
223
224 char *err = NULL;
225 if (mutt_addrlist_to_intl(&al, &err) < 0)
226 {
227 mutt_error(_("Bad IDN: '%s'"), err);
228 FREE(&err);
229 goto done;
230 }
231
232 buf_reset(buf);
233 buf_alloc(buf, 8192);
234 mutt_addrlist_write(&al, buf, true);
235
236 buf_printf(prompt, ngettext("Bounce message to %s?", "Bounce messages to %s?", num_msg),
237 buf_string(buf));
238
239 if (query_quadoption(buf_string(prompt), NeoMutt->sub, "bounce") != MUTT_YES)
240 {
241 msgwin_clear_text(NULL);
242 mutt_message(ngettext("Message not bounced", "Messages not bounced", num_msg));
243 goto done;
244 }
245
246 msgwin_clear_text(NULL);
247
248 int rc = 0;
249 if (b)
250 {
251 rc = mutt_bounce_message(fp, m, b->email, &al, NeoMutt->sub);
252 }
253 else
254 {
255 for (short i = 0; i < actx->idxlen; i++)
256 {
257 if (actx->idx[i]->body->tagged)
258 {
259 if (mutt_bounce_message(actx->idx[i]->fp, m, actx->idx[i]->body->email,
260 &al, NeoMutt->sub))
261 {
262 rc = 1;
263 }
264 }
265 }
266 }
267
268 if (rc == 0)
269 mutt_message(ngettext("Message bounced", "Messages bounced", num_msg));
270 else
271 mutt_error(ngettext("Error bouncing message", "Error bouncing messages", num_msg));
272
273done:
275 buf_pool_release(&buf);
276 buf_pool_release(&prompt);
277}
278
286void mutt_attach_resend(FILE *fp, struct Mailbox *m, struct AttachCtx *actx, struct Body *b)
287{
288 if (!check_all_msg(actx, b, true))
289 return;
290
291 if (b)
292 {
294 }
295 else
296 {
297 for (short i = 0; i < actx->idxlen; i++)
298 {
299 if (actx->idx[i]->body->tagged)
300 {
301 mutt_resend_message(actx->idx[i]->fp, m, actx->idx[i]->body->email,
302 NeoMutt->sub);
303 }
304 }
305 }
306}
307
315static struct AttachPtr *find_common_parent(struct AttachCtx *actx, short nattach)
316{
317 short i;
318 short nchildren;
319
320 for (i = 0; i < actx->idxlen; i++)
321 if (actx->idx[i]->body->tagged)
322 break;
323
324 while (--i >= 0)
325 {
326 if (mutt_is_message_type(actx->idx[i]->body->type, actx->idx[i]->body->subtype))
327 {
328 nchildren = count_tagged_children(actx, i);
329 if (nchildren == nattach)
330 return actx->idx[i];
331 }
332 }
333
334 return NULL;
335}
336
349static int is_parent(short i, struct AttachCtx *actx, const struct Body *b)
350{
351 short level = actx->idx[i]->level;
352
353 while ((++i < actx->idxlen) && (actx->idx[i]->level > level))
354 {
355 if (actx->idx[i]->body == b)
356 return true;
357 }
358
359 return false;
360}
361
370static struct AttachPtr *find_parent(struct AttachCtx *actx, struct Body *b, short nattach)
371{
372 struct AttachPtr *parent = NULL;
373
374 if (b)
375 {
376 for (short i = 0; i < actx->idxlen; i++)
377 {
378 if (mutt_is_message_type(actx->idx[i]->body->type, actx->idx[i]->body->subtype) &&
379 is_parent(i, actx, b))
380 {
381 parent = actx->idx[i];
382 }
383 if (actx->idx[i]->body == b)
384 break;
385 }
386 }
387 else if (nattach)
388 {
389 parent = find_common_parent(actx, nattach);
390 }
391
392 return parent;
393}
394
403static void include_header(bool quote, FILE *fp_in, struct Email *e,
404 FILE *fp_out, const char *prefix)
405{
406 CopyHeaderFlags chflags = CH_DECODE;
407 struct Buffer *prefix2 = buf_pool_get();
408
409 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
410 if (c_weed)
411 chflags |= CH_WEED | CH_REORDER;
412
413 if (quote)
414 {
415 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
416 if (prefix)
417 {
418 buf_strcpy(prefix2, prefix);
419 }
420 else if (!c_text_flowed)
421 {
422 const char *const c_attribution_locale = cs_subset_string(NeoMutt->sub, "attribution_locale");
423 const struct Expando *c_indent_string = cs_subset_expando(NeoMutt->sub, "indent_string");
424 setlocale(LC_TIME, NONULL(c_attribution_locale));
425 mutt_make_string(prefix2, -1, c_indent_string, NULL, -1, e, MUTT_FORMAT_NO_FLAGS, NULL);
426 setlocale(LC_TIME, "");
427 }
428 else
429 {
430 buf_strcpy(prefix2, ">");
431 }
432
433 chflags |= CH_PREFIX;
434 }
435
436 mutt_copy_header(fp_in, e, fp_out, chflags, quote ? buf_string(prefix2) : NULL, 0);
437 buf_pool_release(&prefix2);
438}
439
449static struct Body **copy_problematic_attachments(struct Body **last,
450 struct AttachCtx *actx, bool force)
451{
452 for (short i = 0; i < actx->idxlen; i++)
453 {
454 if (actx->idx[i]->body->tagged && (force || !mutt_can_decode(actx->idx[i]->body)))
455 {
456 if (mutt_body_copy(actx->idx[i]->fp, last, actx->idx[i]->body) == -1)
457 return NULL; /* XXXXX - may lead to crashes */
458 last = &((*last)->next);
459 }
460 }
461 return last;
462}
463
474static void attach_forward_bodies(FILE *fp, struct Email *e, struct AttachCtx *actx,
475 struct Body *b, short nattach)
476{
477 bool mime_fwd_all = false;
478 bool mime_fwd_any = true;
479 struct Email *e_parent = NULL;
480 FILE *fp_parent = NULL;
481 enum QuadOption ans = MUTT_NO;
482 struct Buffer *tempfile = NULL;
483 struct Buffer *prefix = buf_pool_get();
484
485 /* First, find the parent message.
486 * Note: This could be made an option by just
487 * putting the following lines into an if block. */
488 struct AttachPtr *parent = find_parent(actx, b, nattach);
489 if (parent)
490 {
491 e_parent = parent->body->email;
492 fp_parent = parent->fp;
493 }
494 else
495 {
496 e_parent = e;
497 fp_parent = actx->fp_root;
498 }
499
500 struct Email *e_tmp = email_new();
501 e_tmp->env = mutt_env_new();
502 mutt_make_forward_subject(e_tmp->env, e_parent, NeoMutt->sub);
503
504 tempfile = buf_pool_get();
505 buf_mktemp(tempfile);
506 FILE *fp_tmp = mutt_file_fopen(buf_string(tempfile), "w");
507 if (!fp_tmp)
508 {
509 mutt_error(_("Can't open temporary file %s"), buf_string(tempfile));
510 email_free(&e_tmp);
511 goto bail;
512 }
513
514 mutt_forward_intro(e_parent, fp_tmp, NeoMutt->sub);
515
516 /* prepare the prefix here since we'll need it later. */
517
518 const bool c_forward_quote = cs_subset_bool(NeoMutt->sub, "forward_quote");
519 if (c_forward_quote)
520 {
521 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
522 if (c_text_flowed)
523 {
524 buf_strcpy(prefix, ">");
525 }
526 else
527 {
528 const char *const c_attribution_locale = cs_subset_string(NeoMutt->sub, "attribution_locale");
529 const struct Expando *c_indent_string = cs_subset_expando(NeoMutt->sub, "indent_string");
530 setlocale(LC_TIME, NONULL(c_attribution_locale));
531 mutt_make_string(prefix, -1, c_indent_string, NULL, -1, e_parent,
533 setlocale(LC_TIME, "");
534 }
535 }
536
537 include_header(c_forward_quote, fp_parent, e_parent, fp_tmp, buf_string(prefix));
538
539 /* Now, we have prepared the first part of the message body: The
540 * original message's header.
541 *
542 * The next part is more interesting: either include the message bodies,
543 * or attach them. */
544 if ((!b || mutt_can_decode(b)) &&
545 ((ans = query_quadoption(_("Forward as attachments?"), NeoMutt->sub, "mime_forward")) == MUTT_YES))
546 {
547 mime_fwd_all = true;
548 }
549 else if (ans == MUTT_ABORT)
550 {
551 goto bail;
552 }
553
554 /* shortcut MIMEFWDREST when there is only one attachment.
555 * Is this intuitive? */
556 if (!mime_fwd_all && !b && (nattach > 1) && !check_can_decode(actx, b))
557 {
558 ans = query_quadoption(_("Can't decode all tagged attachments. MIME-forward the others?"),
559 NeoMutt->sub, "mime_forward_rest");
560 if (ans == MUTT_ABORT)
561 goto bail;
562 else if (ans == MUTT_NO)
563 mime_fwd_any = false;
564 }
565
566 /* initialize a state structure */
567
568 struct State state = { 0 };
569 if (c_forward_quote)
570 state.prefix = buf_string(prefix);
571 state.flags = STATE_CHARCONV;
572 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
573 if (c_weed)
574 state.flags |= STATE_WEED;
575 state.fp_out = fp_tmp;
576
577 /* where do we append new MIME parts? */
578 struct Body **last = &e_tmp->body;
579
580 if (b)
581 {
582 /* single body case */
583
584 if (!mime_fwd_all && mutt_can_decode(b))
585 {
586 state.fp_in = fp;
587 mutt_body_handler(b, &state);
588 state_putc(&state, '\n');
589 }
590 else
591 {
592 if (mutt_body_copy(fp, last, b) == -1)
593 goto bail;
594 }
595 }
596 else
597 {
598 /* multiple body case */
599
600 if (!mime_fwd_all)
601 {
602 for (int i = 0; i < actx->idxlen; i++)
603 {
604 if (actx->idx[i]->body->tagged && mutt_can_decode(actx->idx[i]->body))
605 {
606 state.fp_in = actx->idx[i]->fp;
607 mutt_body_handler(actx->idx[i]->body, &state);
608 state_putc(&state, '\n');
609 }
610 }
611 }
612
613 if (mime_fwd_any && !copy_problematic_attachments(last, actx, mime_fwd_all))
614 goto bail;
615 }
616
617 mutt_forward_trailer(e_parent, fp_tmp, NeoMutt->sub);
618
619 mutt_file_fclose(&fp_tmp);
620 fp_tmp = NULL;
621
622 /* now that we have the template, send it. */
623 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
624 ARRAY_ADD(&ea, e_parent);
625 mutt_send_message(SEND_NO_FLAGS, e_tmp, buf_string(tempfile), NULL, &ea,
626 NeoMutt->sub);
627 ARRAY_FREE(&ea);
628 buf_pool_release(&tempfile);
629 buf_pool_release(&prefix);
630 return;
631
632bail:
633 if (fp_tmp)
634 {
635 mutt_file_fclose(&fp_tmp);
636 mutt_file_unlink(buf_string(tempfile));
637 }
638 buf_pool_release(&tempfile);
639 buf_pool_release(&prefix);
640
641 email_free(&e_tmp);
642}
643
658static void attach_forward_msgs(FILE *fp, struct AttachCtx *actx, struct Body *b, SendFlags flags)
659{
660 struct Email *e_cur = NULL;
661 struct Email *e_tmp = NULL;
662 enum QuadOption ans;
663 struct Body **last = NULL;
664 struct Buffer *tempfile = NULL;
665 FILE *fp_tmp = NULL;
666
667 CopyHeaderFlags chflags = CH_DECODE;
668
669 if (b)
670 {
671 e_cur = b->email;
672 }
673 else
674 {
675 for (short i = 0; i < actx->idxlen; i++)
676 {
677 if (actx->idx[i]->body->tagged)
678 {
679 e_cur = actx->idx[i]->body->email;
680 break;
681 }
682 }
683 }
684
685 e_tmp = email_new();
686 e_tmp->env = mutt_env_new();
687 mutt_make_forward_subject(e_tmp->env, e_cur, NeoMutt->sub);
688
689 tempfile = buf_pool_get();
690
691 ans = query_quadoption(_("Forward MIME encapsulated?"), NeoMutt->sub, "mime_forward");
692 if (ans == MUTT_NO)
693 {
694 /* no MIME encapsulation */
695
696 buf_mktemp(tempfile);
697 fp_tmp = mutt_file_fopen(buf_string(tempfile), "w");
698 if (!fp_tmp)
699 {
700 mutt_error(_("Can't create %s"), buf_string(tempfile));
701 goto cleanup;
702 }
703
705 const bool c_forward_quote = cs_subset_bool(NeoMutt->sub, "forward_quote");
706 if (c_forward_quote)
707 {
708 chflags |= CH_PREFIX;
709 cmflags |= MUTT_CM_PREFIX;
710 }
711
712 const bool c_forward_decode = cs_subset_bool(NeoMutt->sub, "forward_decode");
713 if (c_forward_decode)
714 {
715 cmflags |= MUTT_CM_DECODE | MUTT_CM_CHARCONV;
716 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
717 if (c_weed)
718 {
719 chflags |= CH_WEED | CH_REORDER;
720 cmflags |= MUTT_CM_WEED;
721 }
722 }
723
724 if (b)
725 {
726 mutt_forward_intro(b->email, fp_tmp, NeoMutt->sub);
727 mutt_copy_message_fp(fp_tmp, fp, b->email, cmflags, chflags, 0);
729 }
730 else
731 {
732 for (short i = 0; i < actx->idxlen; i++)
733 {
734 if (actx->idx[i]->body->tagged)
735 {
736 mutt_forward_intro(actx->idx[i]->body->email, fp_tmp, NeoMutt->sub);
737 mutt_copy_message_fp(fp_tmp, actx->idx[i]->fp,
738 actx->idx[i]->body->email, cmflags, chflags, 0);
739 mutt_forward_trailer(actx->idx[i]->body->email, fp_tmp, NeoMutt->sub);
740 }
741 }
742 }
743 mutt_file_fclose(&fp_tmp);
744 }
745 else if (ans == MUTT_YES) /* do MIME encapsulation - we don't need to do much here */
746 {
747 last = &e_tmp->body;
748 if (b)
749 {
750 mutt_body_copy(fp, last, b);
751 }
752 else
753 {
754 for (short i = 0; i < actx->idxlen; i++)
755 {
756 if (actx->idx[i]->body->tagged)
757 {
758 mutt_body_copy(actx->idx[i]->fp, last, actx->idx[i]->body);
759 last = &((*last)->next);
760 }
761 }
762 }
763 }
764 else
765 {
766 email_free(&e_tmp);
767 }
768
769 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
770 ARRAY_ADD(&ea, e_cur);
771 mutt_send_message(flags, e_tmp, buf_is_empty(tempfile) ? NULL : buf_string(tempfile),
772 NULL, &ea, NeoMutt->sub);
773 ARRAY_FREE(&ea);
774 e_tmp = NULL; /* mutt_send_message frees this */
775
776cleanup:
777 email_free(&e_tmp);
778 buf_pool_release(&tempfile);
779}
780
789void mutt_attach_forward(FILE *fp, struct Email *e, struct AttachCtx *actx,
790 struct Body *b, SendFlags flags)
791{
792 if (check_all_msg(actx, b, false))
793 {
794 attach_forward_msgs(fp, actx, b, flags);
795 }
796 else
797 {
798 const short nattach = count_tagged(actx);
799 attach_forward_bodies(fp, e, actx, b, nattach);
800 }
801}
802
822static int attach_reply_envelope_defaults(struct Envelope *env, struct AttachCtx *actx,
823 struct Email *parent, SendFlags flags)
824{
825 struct Envelope *curenv = NULL;
826 struct Email *e = NULL;
827
828 if (parent)
829 {
830 curenv = parent->env;
831 e = parent;
832 }
833 else
834 {
835 for (short i = 0; i < actx->idxlen; i++)
836 {
837 if (actx->idx[i]->body->tagged)
838 {
839 e = actx->idx[i]->body->email;
840 curenv = e->env;
841 break;
842 }
843 }
844 }
845
846 if (!curenv || !e)
847 {
848 mutt_error(_("Can't find any tagged messages"));
849 return -1;
850 }
851
852 if ((flags & SEND_NEWS))
853 {
854 /* in case followup set Newsgroups: with Followup-To: if it present */
855 if (!env->newsgroups && curenv && !mutt_istr_equal(curenv->followup_to, "poster"))
856 {
858 }
859 }
860 else
861 {
862 if (parent)
863 {
864 if (mutt_fetch_recips(env, curenv, flags, NeoMutt->sub) == -1)
865 return -1;
866 }
867 else
868 {
869 for (short i = 0; i < actx->idxlen; i++)
870 {
871 if (actx->idx[i]->body->tagged &&
872 (mutt_fetch_recips(env, actx->idx[i]->body->email->env, flags,
873 NeoMutt->sub) == -1))
874 {
875 return -1;
876 }
877 }
878 }
879
880 if ((flags & SEND_LIST_REPLY) && TAILQ_EMPTY(&env->to))
881 {
882 mutt_error(_("No mailing lists found"));
883 return -1;
884 }
885
887 }
889
890 if (parent)
891 {
893 }
894 else
895 {
896 for (short i = 0; i < actx->idxlen; i++)
897 {
898 if (actx->idx[i]->body->tagged)
899 {
901 NeoMutt->sub);
902 }
903 }
904 }
905
906 return 0;
907}
908
915static void attach_include_reply(FILE *fp, FILE *fp_tmp, struct Email *e)
916{
918 CopyHeaderFlags chflags = CH_DECODE;
919
921
922 const bool c_header = cs_subset_bool(NeoMutt->sub, "header");
923 if (!c_header)
924 cmflags |= MUTT_CM_NOHEADER;
925 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
926 if (c_weed)
927 {
928 chflags |= CH_WEED;
929 cmflags |= MUTT_CM_WEED;
930 }
931
932 mutt_copy_message_fp(fp_tmp, fp, e, cmflags, chflags, 0);
934}
935
945void mutt_attach_reply(FILE *fp, struct Mailbox *m, struct Email *e,
946 struct AttachCtx *actx, struct Body *b, SendFlags flags)
947{
948 bool mime_reply_any = false;
949
950 short nattach = 0;
951 struct AttachPtr *parent = NULL;
952 struct Email *e_parent = NULL;
953 FILE *fp_parent = NULL;
954 struct Email *e_tmp = NULL;
955 FILE *fp_tmp = NULL;
956 struct Buffer *tempfile = NULL;
957 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
958
959 struct Buffer *prefix = buf_pool_get();
960
961 if (flags & SEND_NEWS)
962 OptNewsSend = true;
963 else
964 OptNewsSend = false;
965
966 if (!check_all_msg(actx, b, false))
967 {
968 nattach = count_tagged(actx);
969 parent = find_parent(actx, b, nattach);
970 if (parent)
971 {
972 e_parent = parent->body->email;
973 fp_parent = parent->fp;
974 }
975 else
976 {
977 e_parent = e;
978 fp_parent = actx->fp_root;
979 }
980 }
981
982 if ((nattach > 1) && !check_can_decode(actx, b))
983 {
984 const enum QuadOption ans = query_quadoption(_("Can't decode all tagged attachments. MIME-encapsulate the others?"),
985 NeoMutt->sub, "mime_forward_rest");
986 if (ans == MUTT_ABORT)
987 goto cleanup;
988 if (ans == MUTT_YES)
989 mime_reply_any = true;
990 }
991 else if (nattach == 1)
992 {
993 mime_reply_any = true;
994 }
995
996 e_tmp = email_new();
997 e_tmp->env = mutt_env_new();
998
999 if (attach_reply_envelope_defaults(e_tmp->env, actx,
1000 e_parent ? e_parent : (b ? b->email : NULL),
1001 flags) == -1)
1002 {
1003 goto cleanup;
1004 }
1005
1006 tempfile = buf_pool_get();
1007 buf_mktemp(tempfile);
1008 fp_tmp = mutt_file_fopen(buf_string(tempfile), "w");
1009 if (!fp_tmp)
1010 {
1011 mutt_error(_("Can't create %s"), buf_string(tempfile));
1012 goto cleanup;
1013 }
1014
1015 if (e_parent)
1016 {
1017 mutt_make_attribution_intro(e_parent, fp_tmp, NeoMutt->sub);
1018
1019 struct State state = { 0 };
1020 state.fp_out = fp_tmp;
1021
1022 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
1023 if (c_text_flowed)
1024 {
1025 buf_strcpy(prefix, ">");
1026 }
1027 else
1028 {
1029 const char *const c_attribution_locale = cs_subset_string(NeoMutt->sub, "attribution_locale");
1030 const struct Expando *c_indent_string = cs_subset_expando(NeoMutt->sub, "indent_string");
1031 setlocale(LC_TIME, NONULL(c_attribution_locale));
1032 mutt_make_string(prefix, -1, c_indent_string, m, -1, e_parent,
1033 MUTT_FORMAT_NO_FLAGS, NULL);
1034 setlocale(LC_TIME, "");
1035 }
1036
1037 state.prefix = buf_string(prefix);
1038 state.flags = STATE_CHARCONV;
1039
1040 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1041 if (c_weed)
1042 state.flags |= STATE_WEED;
1043
1044 const bool c_header = cs_subset_bool(NeoMutt->sub, "header");
1045 if (c_header)
1046 include_header(true, fp_parent, e_parent, fp_tmp, buf_string(prefix));
1047
1048 if (b)
1049 {
1050 if (mutt_can_decode(b))
1051 {
1052 state.fp_in = fp;
1053 mutt_body_handler(b, &state);
1054 state_putc(&state, '\n');
1055 }
1056 else
1057 {
1058 mutt_body_copy(fp, &e_tmp->body, b);
1059 }
1060 }
1061 else
1062 {
1063 for (short i = 0; i < actx->idxlen; i++)
1064 {
1065 if (actx->idx[i]->body->tagged && mutt_can_decode(actx->idx[i]->body))
1066 {
1067 state.fp_in = actx->idx[i]->fp;
1068 mutt_body_handler(actx->idx[i]->body, &state);
1069 state_putc(&state, '\n');
1070 }
1071 }
1072 }
1073
1074 mutt_make_attribution_trailer(e_parent, fp_tmp, NeoMutt->sub);
1075
1076 if (mime_reply_any && !b && !copy_problematic_attachments(&e_tmp->body, actx, false))
1077 {
1078 goto cleanup;
1079 }
1080 }
1081 else
1082 {
1083 if (b)
1084 {
1085 attach_include_reply(fp, fp_tmp, b->email);
1086 }
1087 else
1088 {
1089 for (short i = 0; i < actx->idxlen; i++)
1090 {
1091 if (actx->idx[i]->body->tagged)
1092 attach_include_reply(actx->idx[i]->fp, fp_tmp, actx->idx[i]->body->email);
1093 }
1094 }
1095 }
1096
1097 mutt_file_fclose(&fp_tmp);
1098
1099 ARRAY_ADD(&ea, e_parent ? e_parent : (b ? b->email : NULL));
1100 if (mutt_send_message(flags, e_tmp, buf_string(tempfile), NULL, &ea, NeoMutt->sub) == 0)
1101 {
1102 mutt_set_flag(m, e, MUTT_REPLIED, true, true);
1103 }
1104 e_tmp = NULL; /* mutt_send_message frees this */
1105
1106cleanup:
1107 if (fp_tmp)
1108 {
1109 mutt_file_fclose(&fp_tmp);
1110 mutt_file_unlink(buf_string(tempfile));
1111 }
1112 buf_pool_release(&tempfile);
1113 buf_pool_release(&prefix);
1114 email_free(&e_tmp);
1115 ARRAY_FREE(&ea);
1116}
1117
1123void mutt_attach_mail_sender(struct AttachCtx *actx, struct Body *b)
1124{
1125 if (!check_all_msg(actx, b, 0))
1126 {
1127 /* L10N: You will see this error message if you invoke <compose-to-sender>
1128 when you are on a normal attachment. */
1129 mutt_error(_("You may only compose to sender with message/rfc822 parts"));
1130 return;
1131 }
1132
1133 struct Email *e_tmp = email_new();
1134 e_tmp->env = mutt_env_new();
1135
1136 if (b)
1137 {
1138 if (mutt_fetch_recips(e_tmp->env, b->email->env, SEND_TO_SENDER, NeoMutt->sub) == -1)
1139 {
1140 email_free(&e_tmp);
1141 return;
1142 }
1143 }
1144 else
1145 {
1146 for (int i = 0; i < actx->idxlen; i++)
1147 {
1148 if (actx->idx[i]->body->tagged &&
1149 (mutt_fetch_recips(e_tmp->env, actx->idx[i]->body->email->env,
1150 SEND_TO_SENDER, NeoMutt->sub) == -1))
1151 {
1152 email_free(&e_tmp);
1153 return;
1154 }
1155 }
1156 }
1157
1158 // This call will free e_tmp for us
1159 mutt_send_message(SEND_NO_FLAGS, e_tmp, NULL, NULL, NULL, NeoMutt->sub);
1160}
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition address.c:1464
size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
Write an Address to a buffer.
Definition address.c:1210
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:1297
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_FREE(head)
Release all memory.
Definition array.h:209
#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
#define CH_DECODE
Do RFC2047 header decoding.
Definition copy_email.h:58
#define MUTT_CM_WEED
Weed message/rfc822 attachment headers.
Definition copy_email.h:43
#define MUTT_CM_PREFIX
Quote the header and body.
Definition copy_email.h:39
#define CH_PREFIX
Quote header using $indent_string string?
Definition copy_email.h:61
#define MUTT_CM_DECODE
Decode the message body into text/plain.
Definition copy_email.h:40
#define CH_WEED
Weed the headers?
Definition copy_email.h:57
#define CH_REORDER
Re-order output of headers (specified by 'header-order')
Definition copy_email.h:63
#define MUTT_CM_CHARCONV
Perform character set conversions.
Definition copy_email.h:44
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition copy_email.h:54
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition copy_email.h:37
#define MUTT_CM_NOHEADER
Don't copy the message header.
Definition copy_email.h:38
uint16_t CopyMessageFlags
Flags for mutt_copy_message(), e.g. MUTT_CM_NOHEADER.
Definition copy_email.h:36
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:802
Edit a string.
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition wdata.h:42
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:1506
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:139
#define mutt_file_fopen(PATH, MODE)
Definition file.h:138
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:270
#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:1893
int mutt_body_handler(struct Body *b, struct State *state)
Handler for the Body of an email.
Definition handler.c:1659
Read/write command history from/to a file.
@ HC_ALIAS
Aliases.
Definition lib.h:56
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:518
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
#define STATE_WEED
Weed headers even when not in display mode.
Definition state.h:36
#define state_putc(STATE, STR)
Definition state.h:59
#define STATE_CHARCONV
Do character set conversions.
Definition state.h:37
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:674
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:378
#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:915
static struct Body ** copy_problematic_attachments(struct Body **last, struct AttachCtx *actx, bool force)
Attach the body parts which can't be decoded.
Definition recvcmd.c:449
static bool check_can_decode(struct AttachCtx *actx, struct Body *b)
Can we decode all tagged attachments?
Definition recvcmd.c:110
static void attach_forward_bodies(FILE *fp, struct Email *e, struct AttachCtx *actx, struct Body *b, short nattach)
Forward one or several MIME bodies.
Definition recvcmd.c:474
static short count_tagged(struct AttachCtx *actx)
Count the number of tagged attachments.
Definition recvcmd.c:127
static void attach_forward_msgs(FILE *fp, struct AttachCtx *actx, struct Body *b, SendFlags flags)
Forward one or several message-type attachments.
Definition recvcmd.c:658
static short count_tagged_children(struct AttachCtx *actx, short i)
Tagged children below a multipart/message attachment.
Definition recvcmd.c:143
static struct AttachPtr * find_parent(struct AttachCtx *actx, struct Body *b, short nattach)
Find the parent of an Attachment.
Definition recvcmd.c:370
static struct AttachPtr * find_common_parent(struct AttachCtx *actx, short nattach)
Find a common parent message for the tagged attachments.
Definition recvcmd.c:315
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:403
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:349
void mutt_attach_resend(FILE *fp, struct Mailbox *m, struct AttachCtx *actx, struct Body *b)
Resend-message, from the attachment menu.
Definition recvcmd.c:286
void mutt_attach_mail_sender(struct AttachCtx *actx, struct Body *b)
Compose an email to the sender in the email attachment.
Definition recvcmd.c:1123
void mutt_attach_forward(FILE *fp, struct Email *e, struct AttachCtx *actx, struct Body *b, SendFlags flags)
Forward an Attachment.
Definition recvcmd.c:789
void attach_bounce_message(struct Mailbox *m, FILE *fp, struct AttachCtx *actx, struct Body *b)
Bounce function, from the attachment menu.
Definition recvcmd.c:162
static int attach_reply_envelope_defaults(struct Envelope *env, struct AttachCtx *actx, struct Email *parent, SendFlags flags)
Create the envelope defaults for a reply.
Definition recvcmd.c:822
void mutt_attach_reply(FILE *fp, struct Mailbox *m, struct Email *e, struct AttachCtx *actx, struct Body *b, SendFlags flags)
Attach a reply.
Definition recvcmd.c:945
static bool check_msg(struct Body *b, bool err)
Are we working with an RFC822 message.
Definition recvcmd.c:68
static bool check_all_msg(struct AttachCtx *actx, struct Body *b, bool err)
Are all the Attachments RFC822 messages?
Definition recvcmd.c:86
Send/reply with an attachment.
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition render.h:33
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:1007
void mutt_make_attribution_intro(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add "on DATE, PERSON wrote" header.
Definition send.c:664
void mutt_forward_intro(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add the "start of forwarded message" text.
Definition send.c:456
void mutt_make_forward_subject(struct Envelope *env, struct Email *e, struct ConfigSubset *sub)
Create a subject for a forwarded email.
Definition send.c:987
void mutt_fix_reply_recipients(struct Envelope *env, struct ConfigSubset *sub)
Remove duplicate recipients.
Definition send.c:957
int mutt_resend_message(FILE *fp, struct Mailbox *m, struct Email *e_cur, struct ConfigSubset *sub)
Resend an email.
Definition send.c:1552
int mutt_fetch_recips(struct Envelope *out, struct Envelope *in, SendFlags flags, struct ConfigSubset *sub)
Generate recpients for a reply email.
Definition send.c:874
void mutt_forward_trailer(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add a "end of forwarded message" text.
Definition send.c:480
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:2037
void mutt_make_attribution_trailer(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add suffix to replied email text.
Definition send.c:675
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:1035
uint32_t SendFlags
Flags for mutt_send_message(), e.g. SEND_REPLY.
Definition send.h:40
#define SEND_LIST_REPLY
Reply to mailing list.
Definition send.h:44
#define SEND_TO_SENDER
Compose new email to sender.
Definition send.h:53
#define SEND_NO_FLAGS
No flags are set.
Definition send.h:41
#define SEND_NEWS
Reply to a news article.
Definition send.h:55
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:876
#define NONULL(x)
Definition string2.h:44
A set of attachments.
Definition attach.h:63
FILE * fp_root
Used by recvattach for updating.
Definition attach.h:65
struct AttachPtr ** idx
Array of attachments.
Definition attach.h:67
short idxlen
Number of attachmentes.
Definition attach.h:68
An email to which things will be attached.
Definition attach.h:35
struct Body * body
Attachment.
Definition attach.h:36
int level
Nesting depth of attachment.
Definition attach.h:40
FILE * fp
Used in the recvattach menu.
Definition attach.h:37
The body of an email.
Definition body.h:36
struct Email * email
header information for message/rfc822
Definition body.h:74
bool tagged
This attachment is tagged.
Definition body.h:90
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
struct AddressList from
Email's 'From' list.
Definition envelope.h:59
Parsed Expando trees.
Definition expando.h:41
A mailbox.
Definition mailbox.h:78
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:48
StateFlags flags
Flags, e.g. STATE_DISPLAY.
Definition state.h:52
FILE * fp_out
File to write to.
Definition state.h:50
FILE * fp_in
File to read from.
Definition state.h:49
const char * prefix
String to add to the beginning of each output line.
Definition state.h:51
#define buf_mktemp(buf)
Definition tmp.h:33