NeoMutt  2024-12-12-14-g7b49f7
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
recvcmd.c
Go to the documentation of this file.
1
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 "attach/lib.h"
47#include "editor/lib.h"
48#include "expando/lib.h"
49#include "history/lib.h"
50#include "index/lib.h"
51#include "question/lib.h"
52#include "send/lib.h"
53#include "copy.h"
54#include "globals.h"
55#include "handler.h"
56#include "mutt_body.h"
57#include "mutt_logging.h"
58#include "protos.h"
59#ifdef ENABLE_NLS
60#include <libintl.h>
61#endif
62
72static bool check_msg(struct Body *b, bool err)
73{
75 {
76 if (err)
77 mutt_error(_("You may only bounce message/rfc822 parts"));
78 return false;
79 }
80 return true;
81}
82
90static bool check_all_msg(struct AttachCtx *actx, struct Body *b, bool err)
91{
92 if (b && !check_msg(b, err))
93 return false;
94 if (!b)
95 {
96 for (short i = 0; i < actx->idxlen; i++)
97 {
98 if (actx->idx[i]->body->tagged)
99 {
100 if (!check_msg(actx->idx[i]->body, err))
101 return false;
102 }
103 }
104 }
105 return true;
106}
107
114static bool check_can_decode(struct AttachCtx *actx, struct Body *b)
115{
116 if (b)
117 return mutt_can_decode(b);
118
119 for (short i = 0; i < actx->idxlen; i++)
120 if (actx->idx[i]->body->tagged && !mutt_can_decode(actx->idx[i]->body))
121 return false;
122
123 return true;
124}
125
131static short count_tagged(struct AttachCtx *actx)
132{
133 short count = 0;
134 for (short i = 0; i < actx->idxlen; i++)
135 if (actx->idx[i]->body->tagged)
136 count++;
137
138 return count;
139}
140
147static short count_tagged_children(struct AttachCtx *actx, short i)
148{
149 short level = actx->idx[i]->level;
150 short count = 0;
151
152 while ((++i < actx->idxlen) && (level < actx->idx[i]->level))
153 if (actx->idx[i]->body->tagged)
154 count++;
155
156 return count;
157}
158
166void attach_bounce_message(struct Mailbox *m, FILE *fp, struct AttachCtx *actx,
167 struct Body *b)
168{
169 if (!m || !fp || !actx)
170 return;
171
172 if (!check_all_msg(actx, b, true))
173 return;
174
175 struct AddressList al = TAILQ_HEAD_INITIALIZER(al);
176 struct Buffer *prompt = buf_pool_get();
177 struct Buffer *buf = buf_pool_get();
178
179 /* RFC5322 mandates a From: header, so warn before bouncing
180 * messages without one */
181 if (b)
182 {
183 if (TAILQ_EMPTY(&b->email->env->from))
184 {
185 mutt_error(_("Warning: message contains no From: header"));
187 }
188 }
189 else
190 {
191 for (short i = 0; i < actx->idxlen; i++)
192 {
193 if (actx->idx[i]->body->tagged)
194 {
195 if (TAILQ_EMPTY(&actx->idx[i]->body->email->env->from))
196 {
197 mutt_error(_("Warning: message contains no From: header"));
199 break;
200 }
201 }
202 }
203 }
204
205 /* one or more messages? */
206 int num_msg = b ? 1 : count_tagged(actx);
207 if (num_msg == 1)
208 buf_strcpy(prompt, _("Bounce message to: "));
209 else
210 buf_strcpy(prompt, _("Bounce tagged messages to: "));
211
213 &CompleteAliasOps, NULL) != 0) ||
214 buf_is_empty(buf))
215 {
216 goto done;
217 }
218
220 if (TAILQ_EMPTY(&al))
221 {
222 mutt_error(_("Error parsing address"));
223 goto done;
224 }
225
227
228 char *err = NULL;
229 if (mutt_addrlist_to_intl(&al, &err) < 0)
230 {
231 mutt_error(_("Bad IDN: '%s'"), err);
232 FREE(&err);
233 goto done;
234 }
235
236 buf_reset(buf);
237 buf_alloc(buf, 8192);
238 mutt_addrlist_write(&al, buf, true);
239
240 buf_printf(prompt, ngettext("Bounce message to %s?", "Bounce messages to %s?", num_msg),
241 buf_string(buf));
242
243 if (query_quadoption(buf_string(prompt), NeoMutt->sub, "bounce") != MUTT_YES)
244 {
245 msgwin_clear_text(NULL);
246 mutt_message(ngettext("Message not bounced", "Messages not bounced", num_msg));
247 goto done;
248 }
249
250 msgwin_clear_text(NULL);
251
252 int rc = 0;
253 if (b)
254 {
255 rc = mutt_bounce_message(fp, m, b->email, &al, NeoMutt->sub);
256 }
257 else
258 {
259 for (short i = 0; i < actx->idxlen; i++)
260 {
261 if (actx->idx[i]->body->tagged)
262 {
263 if (mutt_bounce_message(actx->idx[i]->fp, m, actx->idx[i]->body->email,
264 &al, NeoMutt->sub))
265 {
266 rc = 1;
267 }
268 }
269 }
270 }
271
272 if (rc == 0)
273 mutt_message(ngettext("Message bounced", "Messages bounced", num_msg));
274 else
275 mutt_error(ngettext("Error bouncing message", "Error bouncing messages", num_msg));
276
277done:
279 buf_pool_release(&buf);
280 buf_pool_release(&prompt);
281}
282
290void mutt_attach_resend(FILE *fp, struct Mailbox *m, struct AttachCtx *actx, struct Body *b)
291{
292 if (!check_all_msg(actx, b, true))
293 return;
294
295 if (b)
296 {
298 }
299 else
300 {
301 for (short i = 0; i < actx->idxlen; i++)
302 {
303 if (actx->idx[i]->body->tagged)
304 {
305 mutt_resend_message(actx->idx[i]->fp, m, actx->idx[i]->body->email,
306 NeoMutt->sub);
307 }
308 }
309 }
310}
311
319static struct AttachPtr *find_common_parent(struct AttachCtx *actx, short nattach)
320{
321 short i;
322 short nchildren;
323
324 for (i = 0; i < actx->idxlen; i++)
325 if (actx->idx[i]->body->tagged)
326 break;
327
328 while (--i >= 0)
329 {
330 if (mutt_is_message_type(actx->idx[i]->body->type, actx->idx[i]->body->subtype))
331 {
332 nchildren = count_tagged_children(actx, i);
333 if (nchildren == nattach)
334 return actx->idx[i];
335 }
336 }
337
338 return NULL;
339}
340
353static int is_parent(short i, struct AttachCtx *actx, const struct Body *b)
354{
355 short level = actx->idx[i]->level;
356
357 while ((++i < actx->idxlen) && (actx->idx[i]->level > level))
358 {
359 if (actx->idx[i]->body == b)
360 return true;
361 }
362
363 return false;
364}
365
374static struct AttachPtr *find_parent(struct AttachCtx *actx, struct Body *b, short nattach)
375{
376 struct AttachPtr *parent = NULL;
377
378 if (b)
379 {
380 for (short i = 0; i < actx->idxlen; i++)
381 {
382 if (mutt_is_message_type(actx->idx[i]->body->type, actx->idx[i]->body->subtype) &&
383 is_parent(i, actx, b))
384 {
385 parent = actx->idx[i];
386 }
387 if (actx->idx[i]->body == b)
388 break;
389 }
390 }
391 else if (nattach)
392 {
393 parent = find_common_parent(actx, nattach);
394 }
395
396 return parent;
397}
398
407static void include_header(bool quote, FILE *fp_in, struct Email *e,
408 FILE *fp_out, const char *prefix)
409{
410 CopyHeaderFlags chflags = CH_DECODE;
411 struct Buffer *prefix2 = buf_pool_get();
412
413 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
414 if (c_weed)
415 chflags |= CH_WEED | CH_REORDER;
416
417 if (quote)
418 {
419 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
420 if (prefix)
421 {
422 buf_strcpy(prefix2, prefix);
423 }
424 else if (!c_text_flowed)
425 {
426 const char *const c_attribution_locale = cs_subset_string(NeoMutt->sub, "attribution_locale");
427 const struct Expando *c_indent_string = cs_subset_expando(NeoMutt->sub, "indent_string");
428 setlocale(LC_TIME, NONULL(c_attribution_locale));
429 mutt_make_string(prefix2, -1, c_indent_string, NULL, -1, e, MUTT_FORMAT_NO_FLAGS, NULL);
430 setlocale(LC_TIME, "");
431 }
432 else
433 {
434 buf_strcpy(prefix2, ">");
435 }
436
437 chflags |= CH_PREFIX;
438 }
439
440 mutt_copy_header(fp_in, e, fp_out, chflags, quote ? buf_string(prefix2) : NULL, 0);
441 buf_pool_release(&prefix2);
442}
443
453static struct Body **copy_problematic_attachments(struct Body **last,
454 struct AttachCtx *actx, bool force)
455{
456 for (short i = 0; i < actx->idxlen; i++)
457 {
458 if (actx->idx[i]->body->tagged && (force || !mutt_can_decode(actx->idx[i]->body)))
459 {
460 if (mutt_body_copy(actx->idx[i]->fp, last, actx->idx[i]->body) == -1)
461 return NULL; /* XXXXX - may lead to crashes */
462 last = &((*last)->next);
463 }
464 }
465 return last;
466}
467
478static void attach_forward_bodies(FILE *fp, struct Email *e, struct AttachCtx *actx,
479 struct Body *b, short nattach)
480{
481 bool mime_fwd_all = false;
482 bool mime_fwd_any = true;
483 struct Email *e_parent = NULL;
484 FILE *fp_parent = NULL;
485 enum QuadOption ans = MUTT_NO;
486 struct Buffer *tempfile = NULL;
487 struct Buffer *prefix = buf_pool_get();
488
489 /* First, find the parent message.
490 * Note: This could be made an option by just
491 * putting the following lines into an if block. */
492 struct AttachPtr *parent = find_parent(actx, b, nattach);
493 if (parent)
494 {
495 e_parent = parent->body->email;
496 fp_parent = parent->fp;
497 }
498 else
499 {
500 e_parent = e;
501 fp_parent = actx->fp_root;
502 }
503
504 struct Email *e_tmp = email_new();
505 e_tmp->env = mutt_env_new();
506 mutt_make_forward_subject(e_tmp->env, e_parent, NeoMutt->sub);
507
508 tempfile = buf_pool_get();
509 buf_mktemp(tempfile);
510 FILE *fp_tmp = mutt_file_fopen(buf_string(tempfile), "w");
511 if (!fp_tmp)
512 {
513 mutt_error(_("Can't open temporary file %s"), buf_string(tempfile));
514 email_free(&e_tmp);
515 goto bail;
516 }
517
518 mutt_forward_intro(e_parent, fp_tmp, NeoMutt->sub);
519
520 /* prepare the prefix here since we'll need it later. */
521
522 const bool c_forward_quote = cs_subset_bool(NeoMutt->sub, "forward_quote");
523 if (c_forward_quote)
524 {
525 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
526 if (c_text_flowed)
527 {
528 buf_strcpy(prefix, ">");
529 }
530 else
531 {
532 const char *const c_attribution_locale = cs_subset_string(NeoMutt->sub, "attribution_locale");
533 const struct Expando *c_indent_string = cs_subset_expando(NeoMutt->sub, "indent_string");
534 setlocale(LC_TIME, NONULL(c_attribution_locale));
535 mutt_make_string(prefix, -1, c_indent_string, NULL, -1, e_parent,
537 setlocale(LC_TIME, "");
538 }
539 }
540
541 include_header(c_forward_quote, fp_parent, e_parent, fp_tmp, buf_string(prefix));
542
543 /* Now, we have prepared the first part of the message body: The
544 * original message's header.
545 *
546 * The next part is more interesting: either include the message bodies,
547 * or attach them. */
548 if ((!b || mutt_can_decode(b)) &&
549 ((ans = query_quadoption(_("Forward as attachments?"), NeoMutt->sub, "mime_forward")) == MUTT_YES))
550 {
551 mime_fwd_all = true;
552 }
553 else if (ans == MUTT_ABORT)
554 {
555 goto bail;
556 }
557
558 /* shortcut MIMEFWDREST when there is only one attachment.
559 * Is this intuitive? */
560 if (!mime_fwd_all && !b && (nattach > 1) && !check_can_decode(actx, b))
561 {
562 ans = query_quadoption(_("Can't decode all tagged attachments. MIME-forward the others?"),
563 NeoMutt->sub, "mime_forward_rest");
564 if (ans == MUTT_ABORT)
565 goto bail;
566 else if (ans == MUTT_NO)
567 mime_fwd_any = false;
568 }
569
570 /* initialize a state structure */
571
572 struct State state = { 0 };
573 if (c_forward_quote)
574 state.prefix = buf_string(prefix);
575 state.flags = STATE_CHARCONV;
576 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
577 if (c_weed)
578 state.flags |= STATE_WEED;
579 state.fp_out = fp_tmp;
580
581 /* where do we append new MIME parts? */
582 struct Body **last = &e_tmp->body;
583
584 if (b)
585 {
586 /* single body case */
587
588 if (!mime_fwd_all && mutt_can_decode(b))
589 {
590 state.fp_in = fp;
591 mutt_body_handler(b, &state);
592 state_putc(&state, '\n');
593 }
594 else
595 {
596 if (mutt_body_copy(fp, last, b) == -1)
597 goto bail;
598 }
599 }
600 else
601 {
602 /* multiple body case */
603
604 if (!mime_fwd_all)
605 {
606 for (int i = 0; i < actx->idxlen; i++)
607 {
608 if (actx->idx[i]->body->tagged && mutt_can_decode(actx->idx[i]->body))
609 {
610 state.fp_in = actx->idx[i]->fp;
611 mutt_body_handler(actx->idx[i]->body, &state);
612 state_putc(&state, '\n');
613 }
614 }
615 }
616
617 if (mime_fwd_any && !copy_problematic_attachments(last, actx, mime_fwd_all))
618 goto bail;
619 }
620
621 mutt_forward_trailer(e_parent, fp_tmp, NeoMutt->sub);
622
623 mutt_file_fclose(&fp_tmp);
624 fp_tmp = NULL;
625
626 /* now that we have the template, send it. */
627 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
628 ARRAY_ADD(&ea, e_parent);
629 mutt_send_message(SEND_NO_FLAGS, e_tmp, buf_string(tempfile), NULL, &ea,
630 NeoMutt->sub);
631 ARRAY_FREE(&ea);
632 buf_pool_release(&tempfile);
633 buf_pool_release(&prefix);
634 return;
635
636bail:
637 if (fp_tmp)
638 {
639 mutt_file_fclose(&fp_tmp);
640 mutt_file_unlink(buf_string(tempfile));
641 }
642 buf_pool_release(&tempfile);
643 buf_pool_release(&prefix);
644
645 email_free(&e_tmp);
646}
647
662static void attach_forward_msgs(FILE *fp, struct AttachCtx *actx, struct Body *b, SendFlags flags)
663{
664 struct Email *e_cur = NULL;
665 struct Email *e_tmp = NULL;
666 enum QuadOption ans;
667 struct Body **last = NULL;
668 struct Buffer *tempfile = NULL;
669 FILE *fp_tmp = NULL;
670
671 CopyHeaderFlags chflags = CH_DECODE;
672
673 if (b)
674 {
675 e_cur = b->email;
676 }
677 else
678 {
679 for (short i = 0; i < actx->idxlen; i++)
680 {
681 if (actx->idx[i]->body->tagged)
682 {
683 e_cur = actx->idx[i]->body->email;
684 break;
685 }
686 }
687 }
688
689 e_tmp = email_new();
690 e_tmp->env = mutt_env_new();
691 mutt_make_forward_subject(e_tmp->env, e_cur, NeoMutt->sub);
692
693 tempfile = buf_pool_get();
694
695 ans = query_quadoption(_("Forward MIME encapsulated?"), NeoMutt->sub, "mime_forward");
696 if (ans == MUTT_NO)
697 {
698 /* no MIME encapsulation */
699
700 buf_mktemp(tempfile);
701 fp_tmp = mutt_file_fopen(buf_string(tempfile), "w");
702 if (!fp_tmp)
703 {
704 mutt_error(_("Can't create %s"), buf_string(tempfile));
705 goto cleanup;
706 }
707
709 const bool c_forward_quote = cs_subset_bool(NeoMutt->sub, "forward_quote");
710 if (c_forward_quote)
711 {
712 chflags |= CH_PREFIX;
713 cmflags |= MUTT_CM_PREFIX;
714 }
715
716 const bool c_forward_decode = cs_subset_bool(NeoMutt->sub, "forward_decode");
717 if (c_forward_decode)
718 {
719 cmflags |= MUTT_CM_DECODE | MUTT_CM_CHARCONV;
720 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
721 if (c_weed)
722 {
723 chflags |= CH_WEED | CH_REORDER;
724 cmflags |= MUTT_CM_WEED;
725 }
726 }
727
728 if (b)
729 {
730 mutt_forward_intro(b->email, fp_tmp, NeoMutt->sub);
731 mutt_copy_message_fp(fp_tmp, fp, b->email, cmflags, chflags, 0);
733 }
734 else
735 {
736 for (short i = 0; i < actx->idxlen; i++)
737 {
738 if (actx->idx[i]->body->tagged)
739 {
740 mutt_forward_intro(actx->idx[i]->body->email, fp_tmp, NeoMutt->sub);
741 mutt_copy_message_fp(fp_tmp, actx->idx[i]->fp,
742 actx->idx[i]->body->email, cmflags, chflags, 0);
743 mutt_forward_trailer(actx->idx[i]->body->email, fp_tmp, NeoMutt->sub);
744 }
745 }
746 }
747 mutt_file_fclose(&fp_tmp);
748 }
749 else if (ans == MUTT_YES) /* do MIME encapsulation - we don't need to do much here */
750 {
751 last = &e_tmp->body;
752 if (b)
753 {
754 mutt_body_copy(fp, last, b);
755 }
756 else
757 {
758 for (short i = 0; i < actx->idxlen; i++)
759 {
760 if (actx->idx[i]->body->tagged)
761 {
762 mutt_body_copy(actx->idx[i]->fp, last, actx->idx[i]->body);
763 last = &((*last)->next);
764 }
765 }
766 }
767 }
768 else
769 {
770 email_free(&e_tmp);
771 }
772
773 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
774 ARRAY_ADD(&ea, e_cur);
775 mutt_send_message(flags, e_tmp, buf_is_empty(tempfile) ? NULL : buf_string(tempfile),
776 NULL, &ea, NeoMutt->sub);
777 ARRAY_FREE(&ea);
778 e_tmp = NULL; /* mutt_send_message frees this */
779
780cleanup:
781 email_free(&e_tmp);
782 buf_pool_release(&tempfile);
783}
784
793void mutt_attach_forward(FILE *fp, struct Email *e, struct AttachCtx *actx,
794 struct Body *b, SendFlags flags)
795{
796 if (check_all_msg(actx, b, false))
797 {
798 attach_forward_msgs(fp, actx, b, flags);
799 }
800 else
801 {
802 const short nattach = count_tagged(actx);
803 attach_forward_bodies(fp, e, actx, b, nattach);
804 }
805}
806
826static int attach_reply_envelope_defaults(struct Envelope *env, struct AttachCtx *actx,
827 struct Email *parent, SendFlags flags)
828{
829 struct Envelope *curenv = NULL;
830 struct Email *e = NULL;
831
832 if (parent)
833 {
834 curenv = parent->env;
835 e = parent;
836 }
837 else
838 {
839 for (short i = 0; i < actx->idxlen; i++)
840 {
841 if (actx->idx[i]->body->tagged)
842 {
843 e = actx->idx[i]->body->email;
844 curenv = e->env;
845 break;
846 }
847 }
848 }
849
850 if (!curenv || !e)
851 {
852 mutt_error(_("Can't find any tagged messages"));
853 return -1;
854 }
855
856 if ((flags & SEND_NEWS))
857 {
858 /* in case followup set Newsgroups: with Followup-To: if it present */
859 if (!env->newsgroups && curenv && !mutt_istr_equal(curenv->followup_to, "poster"))
860 {
862 }
863 }
864 else
865 {
866 if (parent)
867 {
868 if (mutt_fetch_recips(env, curenv, flags, NeoMutt->sub) == -1)
869 return -1;
870 }
871 else
872 {
873 for (short i = 0; i < actx->idxlen; i++)
874 {
875 if (actx->idx[i]->body->tagged &&
876 (mutt_fetch_recips(env, actx->idx[i]->body->email->env, flags,
877 NeoMutt->sub) == -1))
878 {
879 return -1;
880 }
881 }
882 }
883
884 if ((flags & SEND_LIST_REPLY) && TAILQ_EMPTY(&env->to))
885 {
886 mutt_error(_("No mailing lists found"));
887 return -1;
888 }
889
891 }
893
894 if (parent)
895 {
897 }
898 else
899 {
900 for (short i = 0; i < actx->idxlen; i++)
901 {
902 if (actx->idx[i]->body->tagged)
903 {
905 NeoMutt->sub);
906 }
907 }
908 }
909
910 return 0;
911}
912
919static void attach_include_reply(FILE *fp, FILE *fp_tmp, struct Email *e)
920{
922 CopyHeaderFlags chflags = CH_DECODE;
923
925
926 const bool c_header = cs_subset_bool(NeoMutt->sub, "header");
927 if (!c_header)
928 cmflags |= MUTT_CM_NOHEADER;
929 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
930 if (c_weed)
931 {
932 chflags |= CH_WEED;
933 cmflags |= MUTT_CM_WEED;
934 }
935
936 mutt_copy_message_fp(fp_tmp, fp, e, cmflags, chflags, 0);
938}
939
949void mutt_attach_reply(FILE *fp, struct Mailbox *m, struct Email *e,
950 struct AttachCtx *actx, struct Body *b, SendFlags flags)
951{
952 bool mime_reply_any = false;
953
954 short nattach = 0;
955 struct AttachPtr *parent = NULL;
956 struct Email *e_parent = NULL;
957 FILE *fp_parent = NULL;
958 struct Email *e_tmp = NULL;
959 FILE *fp_tmp = NULL;
960 struct Buffer *tempfile = NULL;
961 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
962
963 struct Buffer *prefix = buf_pool_get();
964
965 if (flags & SEND_NEWS)
966 OptNewsSend = true;
967 else
968 OptNewsSend = false;
969
970 if (!check_all_msg(actx, b, false))
971 {
972 nattach = count_tagged(actx);
973 parent = find_parent(actx, b, nattach);
974 if (parent)
975 {
976 e_parent = parent->body->email;
977 fp_parent = parent->fp;
978 }
979 else
980 {
981 e_parent = e;
982 fp_parent = actx->fp_root;
983 }
984 }
985
986 if ((nattach > 1) && !check_can_decode(actx, b))
987 {
988 const enum QuadOption ans = query_quadoption(_("Can't decode all tagged attachments. MIME-encapsulate the others?"),
989 NeoMutt->sub, "mime_forward_rest");
990 if (ans == MUTT_ABORT)
991 goto cleanup;
992 if (ans == MUTT_YES)
993 mime_reply_any = true;
994 }
995 else if (nattach == 1)
996 {
997 mime_reply_any = true;
998 }
999
1000 e_tmp = email_new();
1001 e_tmp->env = mutt_env_new();
1002
1003 if (attach_reply_envelope_defaults(e_tmp->env, actx,
1004 e_parent ? e_parent : (b ? b->email : NULL),
1005 flags) == -1)
1006 {
1007 goto cleanup;
1008 }
1009
1010 tempfile = buf_pool_get();
1011 buf_mktemp(tempfile);
1012 fp_tmp = mutt_file_fopen(buf_string(tempfile), "w");
1013 if (!fp_tmp)
1014 {
1015 mutt_error(_("Can't create %s"), buf_string(tempfile));
1016 goto cleanup;
1017 }
1018
1019 if (e_parent)
1020 {
1021 mutt_make_attribution_intro(e_parent, fp_tmp, NeoMutt->sub);
1022
1023 struct State state = { 0 };
1024 state.fp_out = fp_tmp;
1025
1026 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
1027 if (c_text_flowed)
1028 {
1029 buf_strcpy(prefix, ">");
1030 }
1031 else
1032 {
1033 const char *const c_attribution_locale = cs_subset_string(NeoMutt->sub, "attribution_locale");
1034 const struct Expando *c_indent_string = cs_subset_expando(NeoMutt->sub, "indent_string");
1035 setlocale(LC_TIME, NONULL(c_attribution_locale));
1036 mutt_make_string(prefix, -1, c_indent_string, m, -1, e_parent,
1037 MUTT_FORMAT_NO_FLAGS, NULL);
1038 setlocale(LC_TIME, "");
1039 }
1040
1041 state.prefix = buf_string(prefix);
1042 state.flags = STATE_CHARCONV;
1043
1044 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1045 if (c_weed)
1046 state.flags |= STATE_WEED;
1047
1048 const bool c_header = cs_subset_bool(NeoMutt->sub, "header");
1049 if (c_header)
1050 include_header(true, fp_parent, e_parent, fp_tmp, buf_string(prefix));
1051
1052 if (b)
1053 {
1054 if (mutt_can_decode(b))
1055 {
1056 state.fp_in = fp;
1057 mutt_body_handler(b, &state);
1058 state_putc(&state, '\n');
1059 }
1060 else
1061 {
1062 mutt_body_copy(fp, &e_tmp->body, b);
1063 }
1064 }
1065 else
1066 {
1067 for (short i = 0; i < actx->idxlen; i++)
1068 {
1069 if (actx->idx[i]->body->tagged && mutt_can_decode(actx->idx[i]->body))
1070 {
1071 state.fp_in = actx->idx[i]->fp;
1072 mutt_body_handler(actx->idx[i]->body, &state);
1073 state_putc(&state, '\n');
1074 }
1075 }
1076 }
1077
1078 mutt_make_attribution_trailer(e_parent, fp_tmp, NeoMutt->sub);
1079
1080 if (mime_reply_any && !b && !copy_problematic_attachments(&e_tmp->body, actx, false))
1081 {
1082 goto cleanup;
1083 }
1084 }
1085 else
1086 {
1087 if (b)
1088 {
1089 attach_include_reply(fp, fp_tmp, b->email);
1090 }
1091 else
1092 {
1093 for (short i = 0; i < actx->idxlen; i++)
1094 {
1095 if (actx->idx[i]->body->tagged)
1096 attach_include_reply(actx->idx[i]->fp, fp_tmp, actx->idx[i]->body->email);
1097 }
1098 }
1099 }
1100
1101 mutt_file_fclose(&fp_tmp);
1102
1103 ARRAY_ADD(&ea, e_parent ? e_parent : (b ? b->email : NULL));
1104 if (mutt_send_message(flags, e_tmp, buf_string(tempfile), NULL, &ea, NeoMutt->sub) == 0)
1105 {
1106 mutt_set_flag(m, e, MUTT_REPLIED, true, true);
1107 }
1108 e_tmp = NULL; /* mutt_send_message frees this */
1109
1110cleanup:
1111 if (fp_tmp)
1112 {
1113 mutt_file_fclose(&fp_tmp);
1114 mutt_file_unlink(buf_string(tempfile));
1115 }
1116 buf_pool_release(&tempfile);
1117 buf_pool_release(&prefix);
1118 email_free(&e_tmp);
1119 ARRAY_FREE(&ea);
1120}
1121
1127void mutt_attach_mail_sender(struct AttachCtx *actx, struct Body *b)
1128{
1129 if (!check_all_msg(actx, b, 0))
1130 {
1131 /* L10N: You will see this error message if you invoke <compose-to-sender>
1132 when you are on a normal attachment. */
1133 mutt_error(_("You may only compose to sender with message/rfc822 parts"));
1134 return;
1135 }
1136
1137 struct Email *e_tmp = email_new();
1138 e_tmp->env = mutt_env_new();
1139
1140 if (b)
1141 {
1142 if (mutt_fetch_recips(e_tmp->env, b->email->env, SEND_TO_SENDER, NeoMutt->sub) == -1)
1143 {
1144 email_free(&e_tmp);
1145 return;
1146 }
1147 }
1148 else
1149 {
1150 for (int i = 0; i < actx->idxlen; i++)
1151 {
1152 if (actx->idx[i]->body->tagged &&
1153 (mutt_fetch_recips(e_tmp->env, actx->idx[i]->body->email->env,
1154 SEND_TO_SENDER, NeoMutt->sub) == -1))
1155 {
1156 email_free(&e_tmp);
1157 return;
1158 }
1159 }
1160 }
1161
1162 // This call will free e_tmp for us
1163 mutt_send_message(SEND_NO_FLAGS, e_tmp, NULL, NULL, NULL, NeoMutt->sub);
1164}
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1460
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_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:1293
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
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition: array.h:156
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:204
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:58
GUI display the mailboxes in a side panel.
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.
Definition: config_type.c:357
Convenience wrapper for the config headers.
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.c:424
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.c:652
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_PREFIX
Quote the header and body.
Definition: copy.h:39
#define CH_PREFIX
Quote header using $indent_string string?
Definition: copy.h:59
#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.
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:803
Edit a string.
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:1498
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition: envelope.c:46
Parse Expando string.
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:221
#define mutt_file_fclose(FP)
Definition: file.h:138
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:137
void mutt_set_flag(struct Mailbox *m, struct Email *e, enum MessageType flag, bool bf, bool upd_mbox)
Set a flag on an email.
Definition: flags.c:57
bool OptNewsSend
(pseudo) used to change behavior when posting
Definition: globals.c:68
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:274
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_message(...)
Definition: logging2.h:91
Convenience wrapper for the gui headers.
bool mutt_can_decode(struct Body *b)
Will decoding the attachment produce any output.
Definition: handler.c:1866
int mutt_body_handler(struct Body *b, struct State *state)
Handler for the Body of an email.
Definition: handler.c:1632
Decide how to display email content.
Read/write command history from/to a file.
@ HC_ALIAS
Aliases.
Definition: lib.h:54
GUI manage the main index (list of emails)
#define FREE(x)
Definition: memory.h:55
void msgwin_clear_text(struct MuttWindow *win)
Clear the text in the Message Window.
Definition: msgwin.c:519
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:672
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
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
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_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:74
NeoMutt Logging.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
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_NO
User answered 'No', or assume 'No'.
Definition: quad.h:38
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:39
Ask the user a question.
enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition: question.c:379
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:655
#define TAILQ_EMPTY(head)
Definition: queue.h:739
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:919
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:453
static bool check_can_decode(struct AttachCtx *actx, struct Body *b)
Can we decode all tagged attachments?
Definition: recvcmd.c:114
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:478
static short count_tagged(struct AttachCtx *actx)
Count the number of tagged attachments.
Definition: recvcmd.c:131
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:662
static short count_tagged_children(struct AttachCtx *actx, short i)
Tagged children below a multipart/message attachment.
Definition: recvcmd.c:147
static struct AttachPtr * find_parent(struct AttachCtx *actx, struct Body *b, short nattach)
Find the parent of an Attachment.
Definition: recvcmd.c:374
static struct AttachPtr * find_common_parent(struct AttachCtx *actx, short nattach)
Find a common parent message for the tagged attachments.
Definition: recvcmd.c:319
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:407
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:353
void mutt_attach_resend(FILE *fp, struct Mailbox *m, struct AttachCtx *actx, struct Body *b)
Resend-message, from the attachment menu.
Definition: recvcmd.c:290
void mutt_attach_mail_sender(struct AttachCtx *actx, struct Body *b)
Compose an email to the sender in the email attachment.
Definition: recvcmd.c:1127
void mutt_attach_forward(FILE *fp, struct Email *e, struct AttachCtx *actx, struct Body *b, SendFlags flags)
Forward an Attachment.
Definition: recvcmd.c:793
void attach_bounce_message(struct Mailbox *m, FILE *fp, struct AttachCtx *actx, struct Body *b)
Bounce function, from the attachment menu.
Definition: recvcmd.c:166
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:826
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:949
static bool check_msg(struct Body *b, bool err)
Are we working with an RFC822 message.
Definition: recvcmd.c:72
static bool check_all_msg(struct AttachCtx *actx, struct Body *b, bool err)
Are all the Attachments RFC822 messages?
Definition: recvcmd.c:90
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:1003
void mutt_make_attribution_intro(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add "on DATE, PERSON wrote" header.
Definition: send.c:661
void mutt_forward_intro(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add the "start of forwarded message" text.
Definition: send.c:453
void mutt_make_forward_subject(struct Envelope *env, struct Email *e, struct ConfigSubset *sub)
Create a subject for a forwarded email.
Definition: send.c:983
void mutt_fix_reply_recipients(struct Envelope *env, struct ConfigSubset *sub)
Remove duplicate recipients.
Definition: send.c:953
int mutt_resend_message(FILE *fp, struct Mailbox *m, struct Email *e_cur, struct ConfigSubset *sub)
Resend an email.
Definition: send.c:1548
int mutt_fetch_recips(struct Envelope *out, struct Envelope *in, SendFlags flags, struct ConfigSubset *sub)
Generate recpients for a reply email.
Definition: send.c:870
void mutt_forward_trailer(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add a "end of forwarded message" text.
Definition: send.c:477
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:2033
void mutt_make_attribution_trailer(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add suffix to replied email text.
Definition: send.c:672
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:1031
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:917
#define NONULL(x)
Definition: string2.h:37
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:79
Container for Accounts, Notifications.
Definition: neomutt.h:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
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