NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
recvcmd.c
Go to the documentation of this file.
1 
30 #include "config.h"
31 #include <stdbool.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include "mutt/lib.h"
35 #include "address/lib.h"
36 #include "config/lib.h"
37 #include "email/lib.h"
38 #include "core/lib.h"
39 #include "alias/lib.h"
40 #include "gui/lib.h"
41 #include "mutt.h"
42 #include "recvcmd.h"
43 #include "question/lib.h"
44 #include "send/lib.h"
45 #include "copy.h"
46 #include "format_flags.h"
47 #include "handler.h"
48 #include "hdrline.h"
49 #include "mutt_body.h"
50 #include "mutt_logging.h"
51 #include "muttlib.h"
52 #include "options.h"
53 #include "protos.h"
54 #ifdef ENABLE_NLS
55 #include <libintl.h>
56 #endif
57 
67 static bool check_msg(struct Body *b, bool err)
68 {
69  if (!mutt_is_message_type(b->type, b->subtype))
70  {
71  if (err)
72  mutt_error(_("You may only bounce message/rfc822 parts"));
73  return false;
74  }
75  return true;
76 }
77 
85 static bool check_all_msg(struct AttachCtx *actx, struct Body *cur, bool err)
86 {
87  if (cur && !check_msg(cur, err))
88  return false;
89  if (!cur)
90  {
91  for (short i = 0; i < actx->idxlen; i++)
92  {
93  if (actx->idx[i]->body->tagged)
94  {
95  if (!check_msg(actx->idx[i]->body, err))
96  return false;
97  }
98  }
99  }
100  return true;
101 }
102 
109 static bool check_can_decode(struct AttachCtx *actx, struct Body *cur)
110 {
111  if (cur)
112  return mutt_can_decode(cur);
113 
114  for (short i = 0; i < actx->idxlen; i++)
115  if (actx->idx[i]->body->tagged && !mutt_can_decode(actx->idx[i]->body))
116  return false;
117 
118  return true;
119 }
120 
126 static short count_tagged(struct AttachCtx *actx)
127 {
128  short count = 0;
129  for (short i = 0; i < actx->idxlen; i++)
130  if (actx->idx[i]->body->tagged)
131  count++;
132 
133  return count;
134 }
135 
142 static short count_tagged_children(struct AttachCtx *actx, short i)
143 {
144  short level = actx->idx[i]->level;
145  short count = 0;
146 
147  while ((++i < actx->idxlen) && (level < actx->idx[i]->level))
148  if (actx->idx[i]->body->tagged)
149  count++;
150 
151  return count;
152 }
153 
161 void mutt_attach_bounce(struct Mailbox *m, FILE *fp, struct AttachCtx *actx, struct Body *cur)
162 {
163  if (!m || !fp || !actx)
164  return;
165 
166  char prompt[256];
167  char buf[8192];
168  char *err = NULL;
169  int ret = 0;
170  int p = 0;
171 
172  if (!check_all_msg(actx, cur, true))
173  return;
174 
175  /* one or more messages? */
176  p = cur ? 1 : count_tagged(actx);
177 
178  /* RFC5322 mandates a From: header, so warn before bouncing
179  * messages without one */
180  if (cur)
181  {
182  if (TAILQ_EMPTY(&cur->email->env->from))
183  {
184  mutt_error(_("Warning: message contains no From: header"));
186  }
187  }
188  else
189  {
190  for (short i = 0; i < actx->idxlen; i++)
191  {
192  if (actx->idx[i]->body->tagged)
193  {
194  if (TAILQ_EMPTY(&actx->idx[i]->body->email->env->from))
195  {
196  mutt_error(_("Warning: message contains no From: header"));
198  break;
199  }
200  }
201  }
202  }
203 
204  if (p)
205  mutt_str_copy(prompt, _("Bounce message to: "), sizeof(prompt));
206  else
207  mutt_str_copy(prompt, _("Bounce tagged messages to: "), sizeof(prompt));
208 
209  buf[0] = '\0';
210  if (mutt_get_field(prompt, buf, sizeof(buf), MUTT_ALIAS, false, NULL, NULL) ||
211  (buf[0] == '\0'))
212  {
213  return;
214  }
215 
216  struct AddressList al = TAILQ_HEAD_INITIALIZER(al);
217  mutt_addrlist_parse(&al, buf);
218  if (TAILQ_EMPTY(&al))
219  {
220  mutt_error(_("Error parsing address"));
221  return;
222  }
223 
224  mutt_expand_aliases(&al);
225 
226  if (mutt_addrlist_to_intl(&al, &err) < 0)
227  {
228  mutt_error(_("Bad IDN: '%s'"), err);
229  FREE(&err);
230  goto end;
231  }
232 
233  buf[0] = '\0';
234  mutt_addrlist_write(&al, buf, sizeof(buf), true);
235 
236 #define EXTRA_SPACE (15 + 7 + 2)
237  /* See commands.c. */
238  snprintf(prompt, sizeof(prompt) - 4,
239  ngettext("Bounce message to %s?", "Bounce messages to %s?", p), buf);
240 
241  const size_t width = msgwin_get_width();
242  if (mutt_strwidth(prompt) > (width - EXTRA_SPACE))
243  {
244  mutt_simple_format(prompt, sizeof(prompt) - 4, 0, width - EXTRA_SPACE,
245  JUSTIFY_LEFT, 0, prompt, sizeof(prompt), false);
246  mutt_str_cat(prompt, sizeof(prompt), "...?");
247  }
248  else
249  mutt_str_cat(prompt, sizeof(prompt), "?");
250 
251  const enum QuadOption c_bounce = cs_subset_quad(NeoMutt->sub, "bounce");
252  if (query_quadoption(c_bounce, prompt) != MUTT_YES)
253  {
255  mutt_message(ngettext("Message not bounced", "Messages not bounced", p));
256  goto end;
257  }
258 
260 
261  if (cur)
262  ret = mutt_bounce_message(fp, m, cur->email, &al, NeoMutt->sub);
263  else
264  {
265  for (short i = 0; i < actx->idxlen; i++)
266  {
267  if (actx->idx[i]->body->tagged)
268  {
269  if (mutt_bounce_message(actx->idx[i]->fp, m, actx->idx[i]->body->email,
270  &al, NeoMutt->sub))
271  {
272  ret = 1;
273  }
274  }
275  }
276  }
277 
278  if (ret == 0)
279  mutt_message(ngettext("Message bounced", "Messages bounced", p));
280  else
281  mutt_error(ngettext("Error bouncing message", "Error bouncing messages", p));
282 
283 end:
284  mutt_addrlist_clear(&al);
285 }
286 
294 void mutt_attach_resend(FILE *fp, struct Mailbox *m, struct AttachCtx *actx, struct Body *cur)
295 {
296  if (!check_all_msg(actx, cur, true))
297  return;
298 
299  if (cur)
300  mutt_resend_message(fp, m, cur->email, NeoMutt->sub);
301  else
302  {
303  for (short i = 0; i < actx->idxlen; i++)
304  {
305  if (actx->idx[i]->body->tagged)
306  {
307  mutt_resend_message(actx->idx[i]->fp, m, actx->idx[i]->body->email,
308  NeoMutt->sub);
309  }
310  }
311  }
312 }
313 
321 static struct AttachPtr *find_common_parent(struct AttachCtx *actx, short nattach)
322 {
323  short i;
324  short nchildren;
325 
326  for (i = 0; i < actx->idxlen; i++)
327  if (actx->idx[i]->body->tagged)
328  break;
329 
330  while (--i >= 0)
331  {
332  if (mutt_is_message_type(actx->idx[i]->body->type, actx->idx[i]->body->subtype))
333  {
334  nchildren = count_tagged_children(actx, i);
335  if (nchildren == nattach)
336  return actx->idx[i];
337  }
338  }
339 
340  return NULL;
341 }
342 
355 static int is_parent(short i, struct AttachCtx *actx, struct Body *cur)
356 {
357  short level = actx->idx[i]->level;
358 
359  while ((++i < actx->idxlen) && (actx->idx[i]->level > level))
360  {
361  if (actx->idx[i]->body == cur)
362  return true;
363  }
364 
365  return false;
366 }
367 
376 static struct AttachPtr *find_parent(struct AttachCtx *actx, struct Body *cur, short nattach)
377 {
378  struct AttachPtr *parent = NULL;
379 
380  if (cur)
381  {
382  for (short i = 0; i < actx->idxlen; i++)
383  {
384  if (mutt_is_message_type(actx->idx[i]->body->type, actx->idx[i]->body->subtype) &&
385  is_parent(i, actx, cur))
386  {
387  parent = actx->idx[i];
388  }
389  if (actx->idx[i]->body == cur)
390  break;
391  }
392  }
393  else if (nattach)
394  parent = find_common_parent(actx, nattach);
395 
396  return parent;
397 }
398 
407 static void include_header(bool quote, FILE *fp_in, struct Email *e, FILE *fp_out, char *prefix)
408 {
409  CopyHeaderFlags chflags = CH_DECODE;
410  char prefix2[128];
411 
412  const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
413  if (c_weed)
414  chflags |= CH_WEED | CH_REORDER;
415 
416  if (quote)
417  {
418  const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
419  if (prefix)
420  mutt_str_copy(prefix2, prefix, sizeof(prefix2));
421  else if (!c_text_flowed)
422  {
423  const char *const c_indent_string =
424  cs_subset_string(NeoMutt->sub, "indent_string");
425  mutt_make_string(prefix2, sizeof(prefix2), 0, NONULL(c_indent_string),
426  NULL, -1, e, MUTT_FORMAT_NO_FLAGS, NULL);
427  }
428  else
429  mutt_str_copy(prefix2, ">", sizeof(prefix2));
430 
431  chflags |= CH_PREFIX;
432  }
433 
434  mutt_copy_header(fp_in, e, fp_out, chflags, quote ? prefix2 : NULL, 0);
435 }
436 
446 static struct Body **copy_problematic_attachments(struct Body **last,
447  struct AttachCtx *actx, bool force)
448 {
449  for (short i = 0; i < actx->idxlen; i++)
450  {
451  if (actx->idx[i]->body->tagged && (force || !mutt_can_decode(actx->idx[i]->body)))
452  {
453  if (mutt_body_copy(actx->idx[i]->fp, last, actx->idx[i]->body) == -1)
454  return NULL; /* XXXXX - may lead to crashes */
455  last = &((*last)->next);
456  }
457  }
458  return last;
459 }
460 
471 static void attach_forward_bodies(FILE *fp, struct Email *e, struct AttachCtx *actx,
472  struct Body *cur, short nattach)
473 {
474  bool mime_fwd_all = false;
475  bool mime_fwd_any = true;
476  struct Email *e_parent = NULL;
477  FILE *fp_parent = NULL;
478  char prefix[256];
479  enum QuadOption ans = MUTT_NO;
480  struct Buffer *tmpbody = NULL;
481 
482  /* First, find the parent message.
483  * Note: This could be made an option by just
484  * putting the following lines into an if block. */
485  struct AttachPtr *parent = find_parent(actx, cur, nattach);
486  if (parent)
487  {
488  e_parent = parent->body->email;
489  fp_parent = parent->fp;
490  }
491  else
492  {
493  e_parent = e;
494  fp_parent = actx->fp_root;
495  }
496 
497  struct Email *e_tmp = email_new();
498  e_tmp->env = mutt_env_new();
499  mutt_make_forward_subject(e_tmp->env, e_parent, NeoMutt->sub);
500 
501  tmpbody = mutt_buffer_pool_get();
502  mutt_buffer_mktemp(tmpbody);
503  FILE *fp_tmp = mutt_file_fopen(mutt_buffer_string(tmpbody), "w");
504  if (!fp_tmp)
505  {
506  mutt_error(_("Can't open temporary file %s"), mutt_buffer_string(tmpbody));
507  email_free(&e_tmp);
508  goto bail;
509  }
510 
511  mutt_forward_intro(e_parent, fp_tmp, NeoMutt->sub);
512 
513  /* prepare the prefix here since we'll need it later. */
514 
515  const bool c_forward_quote = cs_subset_bool(NeoMutt->sub, "forward_quote");
516  if (c_forward_quote)
517  {
518  const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
519  if (c_text_flowed)
520  mutt_str_copy(prefix, ">", sizeof(prefix));
521  else
522  {
523  const char *const c_indent_string =
524  cs_subset_string(NeoMutt->sub, "indent_string");
525  mutt_make_string(prefix, sizeof(prefix), 0, NONULL(c_indent_string), NULL,
526  -1, e_parent, MUTT_FORMAT_NO_FLAGS, NULL);
527  }
528  }
529 
530  include_header(c_forward_quote, fp_parent, e_parent, fp_tmp, prefix);
531 
532  /* Now, we have prepared the first part of the message body: The
533  * original message's header.
534  *
535  * The next part is more interesting: either include the message bodies,
536  * or attach them. */
537  const enum QuadOption c_mime_forward =
538  cs_subset_quad(NeoMutt->sub, "mime_forward");
539  if ((!cur || mutt_can_decode(cur)) &&
540  ((ans = query_quadoption(c_mime_forward, _("Forward as attachments?"))) == MUTT_YES))
541  {
542  mime_fwd_all = true;
543  }
544  else if (ans == MUTT_ABORT)
545  {
546  goto bail;
547  }
548 
549  /* shortcut MIMEFWDREST when there is only one attachment.
550  * Is this intuitive? */
551  if (!mime_fwd_all && !cur && (nattach > 1) && !check_can_decode(actx, cur))
552  {
553  const enum QuadOption c_mime_forward_rest =
554  cs_subset_quad(NeoMutt->sub, "mime_forward_rest");
555  ans = query_quadoption(
556  c_mime_forward_rest,
557  _("Can't decode all tagged attachments. MIME-forward the others?"));
558  if (ans == MUTT_ABORT)
559  goto bail;
560  else if (ans == MUTT_NO)
561  mime_fwd_any = false;
562  }
563 
564  /* initialize a state structure */
565 
566  struct State st = { 0 };
567  if (c_forward_quote)
568  st.prefix = prefix;
569  st.flags = MUTT_CHARCONV;
570  const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
571  if (c_weed)
572  st.flags |= MUTT_WEED;
573  st.fp_out = fp_tmp;
574 
575  /* where do we append new MIME parts? */
576  struct Body **last = &e_tmp->body;
577 
578  if (cur)
579  {
580  /* single body case */
581 
582  if (!mime_fwd_all && mutt_can_decode(cur))
583  {
584  st.fp_in = fp;
585  mutt_body_handler(cur, &st);
586  state_putc(&st, '\n');
587  }
588  else
589  {
590  if (mutt_body_copy(fp, last, cur) == -1)
591  goto bail;
592  }
593  }
594  else
595  {
596  /* multiple body case */
597 
598  if (!mime_fwd_all)
599  {
600  for (int i = 0; i < actx->idxlen; i++)
601  {
602  if (actx->idx[i]->body->tagged && mutt_can_decode(actx->idx[i]->body))
603  {
604  st.fp_in = actx->idx[i]->fp;
605  mutt_body_handler(actx->idx[i]->body, &st);
606  state_putc(&st, '\n');
607  }
608  }
609  }
610 
611  if (mime_fwd_any && !copy_problematic_attachments(last, actx, mime_fwd_all))
612  goto bail;
613  }
614 
615  mutt_forward_trailer(e_parent, fp_tmp, NeoMutt->sub);
616 
617  mutt_file_fclose(&fp_tmp);
618  fp_tmp = NULL;
619 
620  /* now that we have the template, send it. */
621  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
622  emaillist_add_email(&el, e_parent);
623  mutt_send_message(SEND_NO_FLAGS, e_tmp, mutt_buffer_string(tmpbody), NULL,
624  &el, NeoMutt->sub);
625  emaillist_clear(&el);
626  mutt_buffer_pool_release(&tmpbody);
627  return;
628 
629 bail:
630  if (fp_tmp)
631  {
632  mutt_file_fclose(&fp_tmp);
634  }
635  mutt_buffer_pool_release(&tmpbody);
636 
637  email_free(&e_tmp);
638 }
639 
654 static void attach_forward_msgs(FILE *fp, struct AttachCtx *actx,
655  struct Body *cur, SendFlags flags)
656 {
657  struct Email *e_cur = NULL;
658  struct Email *e_tmp = NULL;
659  enum QuadOption ans;
660  struct Body **last = NULL;
661  struct Buffer *tmpbody = NULL;
662  FILE *fp_tmp = NULL;
663 
664  CopyHeaderFlags chflags = CH_XMIT;
665 
666  if (cur)
667  e_cur = cur->email;
668  else
669  {
670  for (short i = 0; i < actx->idxlen; i++)
671  {
672  if (actx->idx[i]->body->tagged)
673  {
674  e_cur = actx->idx[i]->body->email;
675  break;
676  }
677  }
678  }
679 
680  e_tmp = email_new();
681  e_tmp->env = mutt_env_new();
682  mutt_make_forward_subject(e_tmp->env, e_cur, NeoMutt->sub);
683 
684  tmpbody = mutt_buffer_pool_get();
685 
686  const enum QuadOption c_mime_forward =
687  cs_subset_quad(NeoMutt->sub, "mime_forward");
688  ans = query_quadoption(c_mime_forward, _("Forward MIME encapsulated?"));
689  if (ans == MUTT_NO)
690  {
691  /* no MIME encapsulation */
692 
693  mutt_buffer_mktemp(tmpbody);
694  fp_tmp = mutt_file_fopen(mutt_buffer_string(tmpbody), "w");
695  if (!fp_tmp)
696  {
697  mutt_error(_("Can't create %s"), mutt_buffer_string(tmpbody));
698  goto cleanup;
699  }
700 
702  const bool c_forward_quote = cs_subset_bool(NeoMutt->sub, "forward_quote");
703  if (c_forward_quote)
704  {
705  chflags |= CH_PREFIX;
706  cmflags |= MUTT_CM_PREFIX;
707  }
708 
709  const bool c_forward_decode =
710  cs_subset_bool(NeoMutt->sub, "forward_decode");
711  if (c_forward_decode)
712  {
713  cmflags |= MUTT_CM_DECODE | MUTT_CM_CHARCONV;
714  const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
715  if (c_weed)
716  {
717  chflags |= CH_WEED | CH_REORDER;
718  cmflags |= MUTT_CM_WEED;
719  }
720  }
721 
722  if (cur)
723  {
724  mutt_forward_intro(cur->email, fp_tmp, NeoMutt->sub);
725  mutt_copy_message_fp(fp_tmp, fp, cur->email, cmflags, chflags, 0);
726  mutt_forward_trailer(cur->email, fp_tmp, NeoMutt->sub);
727  }
728  else
729  {
730  for (short i = 0; i < actx->idxlen; i++)
731  {
732  if (actx->idx[i]->body->tagged)
733  {
734  mutt_forward_intro(actx->idx[i]->body->email, fp_tmp, NeoMutt->sub);
735  mutt_copy_message_fp(fp_tmp, actx->idx[i]->fp,
736  actx->idx[i]->body->email, cmflags, chflags, 0);
737  mutt_forward_trailer(actx->idx[i]->body->email, fp_tmp, NeoMutt->sub);
738  }
739  }
740  }
741  mutt_file_fclose(&fp_tmp);
742  }
743  else if (ans == MUTT_YES) /* do MIME encapsulation - we don't need to do much here */
744  {
745  last = &e_tmp->body;
746  if (cur)
747  mutt_body_copy(fp, last, cur);
748  else
749  {
750  for (short i = 0; i < actx->idxlen; i++)
751  {
752  if (actx->idx[i]->body->tagged)
753  {
754  mutt_body_copy(actx->idx[i]->fp, last, actx->idx[i]->body);
755  last = &((*last)->next);
756  }
757  }
758  }
759  }
760  else
761  email_free(&e_tmp);
762 
763  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
764  emaillist_add_email(&el, e_cur);
765  mutt_send_message(flags, e_tmp,
766  mutt_buffer_is_empty(tmpbody) ? NULL : mutt_buffer_string(tmpbody),
767  NULL, &el, NeoMutt->sub);
768  emaillist_clear(&el);
769  e_tmp = NULL; /* mutt_send_message frees this */
770 
771 cleanup:
772  email_free(&e_tmp);
773  mutt_buffer_pool_release(&tmpbody);
774 }
775 
784 void mutt_attach_forward(FILE *fp, struct Email *e, struct AttachCtx *actx,
785  struct Body *cur, SendFlags flags)
786 {
787  if (check_all_msg(actx, cur, false))
788  attach_forward_msgs(fp, actx, cur, flags);
789  else
790  {
791  const short nattach = count_tagged(actx);
792  attach_forward_bodies(fp, e, actx, cur, nattach);
793  }
794 }
795 
815 static int attach_reply_envelope_defaults(struct Envelope *env, struct AttachCtx *actx,
816  struct Email *parent, SendFlags flags)
817 {
818  struct Envelope *curenv = NULL;
819  struct Email *e = NULL;
820 
821  if (!parent)
822  {
823  for (short i = 0; i < actx->idxlen; i++)
824  {
825  if (actx->idx[i]->body->tagged)
826  {
827  e = actx->idx[i]->body->email;
828  curenv = e->env;
829  break;
830  }
831  }
832  }
833  else
834  {
835  curenv = parent->env;
836  e = parent;
837  }
838 
839  if (!curenv || !e)
840  {
841  mutt_error(_("Can't find any tagged messages"));
842  return -1;
843  }
844 
845 #ifdef USE_NNTP
846  if ((flags & SEND_NEWS))
847  {
848  /* in case followup set Newsgroups: with Followup-To: if it present */
849  if (!env->newsgroups && curenv && !mutt_istr_equal(curenv->followup_to, "poster"))
850  {
851  env->newsgroups = mutt_str_dup(curenv->followup_to);
852  }
853  }
854  else
855 #endif
856  {
857  if (parent)
858  {
859  if (mutt_fetch_recips(env, curenv, flags, NeoMutt->sub) == -1)
860  return -1;
861  }
862  else
863  {
864  for (short i = 0; i < actx->idxlen; i++)
865  {
866  if (actx->idx[i]->body->tagged &&
867  (mutt_fetch_recips(env, actx->idx[i]->body->email->env, flags,
868  NeoMutt->sub) == -1))
869  {
870  return -1;
871  }
872  }
873  }
874 
875  if ((flags & SEND_LIST_REPLY) && TAILQ_EMPTY(&env->to))
876  {
877  mutt_error(_("No mailing lists found"));
878  return -1;
879  }
880 
882  }
884 
885  if (parent)
887  else
888  {
889  for (short i = 0; i < actx->idxlen; i++)
890  {
891  if (actx->idx[i]->body->tagged)
892  {
894  NeoMutt->sub);
895  }
896  }
897  }
898 
899  return 0;
900 }
901 
908 static void attach_include_reply(FILE *fp, FILE *fp_tmp, struct Email *e)
909 {
911  CopyHeaderFlags chflags = CH_DECODE;
912 
913  mutt_make_attribution(e, fp_tmp, NeoMutt->sub);
914 
915  const bool c_header = cs_subset_bool(NeoMutt->sub, "header");
916  if (!c_header)
917  cmflags |= MUTT_CM_NOHEADER;
918  const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
919  if (c_weed)
920  {
921  chflags |= CH_WEED;
922  cmflags |= MUTT_CM_WEED;
923  }
924 
925  mutt_copy_message_fp(fp_tmp, fp, e, cmflags, chflags, 0);
926  mutt_make_post_indent(e, fp_tmp, NeoMutt->sub);
927 }
928 
938 void mutt_attach_reply(FILE *fp, struct Mailbox *m, struct Email *e,
939  struct AttachCtx *actx, struct Body *e_cur, SendFlags flags)
940 {
941  bool mime_reply_any = false;
942 
943  short nattach = 0;
944  struct AttachPtr *parent = NULL;
945  struct Email *e_parent = NULL;
946  FILE *fp_parent = NULL;
947  struct Email *e_tmp = NULL;
948  FILE *fp_tmp = NULL;
949  struct Buffer *tmpbody = NULL;
950  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
951 
952  char prefix[128];
953 
954 #ifdef USE_NNTP
955  if (flags & SEND_NEWS)
956  OptNewsSend = true;
957  else
958  OptNewsSend = false;
959 #endif
960 
961  if (!check_all_msg(actx, e_cur, false))
962  {
963  nattach = count_tagged(actx);
964  parent = find_parent(actx, e_cur, nattach);
965  if (parent)
966  {
967  e_parent = parent->body->email;
968  fp_parent = parent->fp;
969  }
970  else
971  {
972  e_parent = e;
973  fp_parent = actx->fp_root;
974  }
975  }
976 
977  if ((nattach > 1) && !check_can_decode(actx, e_cur))
978  {
979  const enum QuadOption c_mime_forward_rest =
980  cs_subset_quad(NeoMutt->sub, "mime_forward_rest");
981  const enum QuadOption ans = query_quadoption(
982  c_mime_forward_rest, _("Can't decode all tagged attachments. "
983  "MIME-encapsulate the others?"));
984  if (ans == MUTT_ABORT)
985  return;
986  if (ans == MUTT_YES)
987  mime_reply_any = true;
988  }
989  else if (nattach == 1)
990  mime_reply_any = true;
991 
992  e_tmp = email_new();
993  e_tmp->env = mutt_env_new();
994 
996  e_tmp->env, actx, e_parent ? e_parent : (e_cur ? e_cur->email : NULL), flags) == -1)
997  {
998  goto cleanup;
999  }
1000 
1001  tmpbody = mutt_buffer_pool_get();
1002  mutt_buffer_mktemp(tmpbody);
1003  fp_tmp = mutt_file_fopen(mutt_buffer_string(tmpbody), "w");
1004  if (!fp_tmp)
1005  {
1006  mutt_error(_("Can't create %s"), mutt_buffer_string(tmpbody));
1007  goto cleanup;
1008  }
1009 
1010  if (!e_parent)
1011  {
1012  if (e_cur)
1013  attach_include_reply(fp, fp_tmp, e_cur->email);
1014  else
1015  {
1016  for (short i = 0; i < actx->idxlen; i++)
1017  {
1018  if (actx->idx[i]->body->tagged)
1019  attach_include_reply(actx->idx[i]->fp, fp_tmp, actx->idx[i]->body->email);
1020  }
1021  }
1022  }
1023  else
1024  {
1025  mutt_make_attribution(e_parent, fp_tmp, NeoMutt->sub);
1026 
1027  struct State st;
1028  memset(&st, 0, sizeof(struct State));
1029  st.fp_out = fp_tmp;
1030 
1031  const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
1032  if (c_text_flowed)
1033  {
1034  mutt_str_copy(prefix, ">", sizeof(prefix));
1035  }
1036  else
1037  {
1038  const char *const c_indent_string =
1039  cs_subset_string(NeoMutt->sub, "indent_string");
1040  mutt_make_string(prefix, sizeof(prefix), 0, NONULL(c_indent_string), m,
1041  -1, e_parent, MUTT_FORMAT_NO_FLAGS, NULL);
1042  }
1043 
1044  st.prefix = prefix;
1045  st.flags = MUTT_CHARCONV;
1046 
1047  const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1048  if (c_weed)
1049  st.flags |= MUTT_WEED;
1050 
1051  const bool c_header = cs_subset_bool(NeoMutt->sub, "header");
1052  if (c_header)
1053  include_header(true, fp_parent, e_parent, fp_tmp, prefix);
1054 
1055  if (e_cur)
1056  {
1057  if (mutt_can_decode(e_cur))
1058  {
1059  st.fp_in = fp;
1060  mutt_body_handler(e_cur, &st);
1061  state_putc(&st, '\n');
1062  }
1063  else
1064  mutt_body_copy(fp, &e_tmp->body, e_cur);
1065  }
1066  else
1067  {
1068  for (short i = 0; i < actx->idxlen; i++)
1069  {
1070  if (actx->idx[i]->body->tagged && mutt_can_decode(actx->idx[i]->body))
1071  {
1072  st.fp_in = actx->idx[i]->fp;
1073  mutt_body_handler(actx->idx[i]->body, &st);
1074  state_putc(&st, '\n');
1075  }
1076  }
1077  }
1078 
1079  mutt_make_post_indent(e_parent, fp_tmp, NeoMutt->sub);
1080 
1081  if (mime_reply_any && !e_cur && !copy_problematic_attachments(&e_tmp->body, actx, false))
1082  {
1083  goto cleanup;
1084  }
1085  }
1086 
1087  mutt_file_fclose(&fp_tmp);
1088 
1089  emaillist_add_email(&el, e_parent ? e_parent : (e_cur ? e_cur->email : NULL));
1090  if (mutt_send_message(flags, e_tmp, mutt_buffer_string(tmpbody), NULL, &el,
1091  NeoMutt->sub) == 0)
1092  {
1093  mutt_set_flag(m, e, MUTT_REPLIED, true);
1094  }
1095  e_tmp = NULL; /* mutt_send_message frees this */
1096 
1097 cleanup:
1098  if (fp_tmp)
1099  {
1100  mutt_file_fclose(&fp_tmp);
1102  }
1103  mutt_buffer_pool_release(&tmpbody);
1104  email_free(&e_tmp);
1105  emaillist_clear(&el);
1106 }
1107 
1115 void mutt_attach_mail_sender(FILE *fp, struct Email *e, struct AttachCtx *actx,
1116  struct Body *cur)
1117 {
1118  if (!check_all_msg(actx, cur, 0))
1119  {
1120  /* L10N: You will see this error message if you invoke <compose-to-sender>
1121  when you are on a normal attachment. */
1122  mutt_error(_("You may only compose to sender with message/rfc822 parts"));
1123  return;
1124  }
1125 
1126  struct Email *e_tmp = email_new();
1127  e_tmp->env = mutt_env_new();
1128 
1129  if (cur)
1130  {
1131  if (mutt_fetch_recips(e_tmp->env, cur->email->env, SEND_TO_SENDER, NeoMutt->sub) == -1)
1132  {
1133  email_free(&e_tmp);
1134  return;
1135  }
1136  }
1137  else
1138  {
1139  for (int i = 0; i < actx->idxlen; i++)
1140  {
1141  if (actx->idx[i]->body->tagged &&
1142  (mutt_fetch_recips(e_tmp->env, actx->idx[i]->body->email->env,
1143  SEND_TO_SENDER, NeoMutt->sub) == -1))
1144  {
1145  email_free(&e_tmp);
1146  return;
1147  }
1148  }
1149  }
1150 
1151  // This call will free e_tmp for us
1152  mutt_send_message(SEND_NO_FLAGS, e_tmp, NULL, NULL, NULL, NeoMutt->sub);
1153 }
Convenience wrapper for the gui headers.
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
#define NONULL(x)
Definition: string2.h:37
An email to which things will be attached.
Definition: attach.h:34
static bool check_all_msg(struct AttachCtx *actx, struct Body *cur, bool err)
Are all the Attachments RFC822 messages?
Definition: recvcmd.c:85
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:66
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
void mutt_expand_aliases(struct AddressList *al)
Expand aliases in a List of Addresses.
Definition: alias.c:300
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1385
#define SEND_TO_SENDER
Compose new email to sender.
Definition: send.h:51
Left justify the text.
Definition: curs_lib.h:43
The envelope/body of an email.
Definition: email.h:37
static void include_header(bool quote, FILE *fp_in, struct Email *e, FILE *fp_out, char *prefix)
Write an email header to a file, optionally quoting it.
Definition: recvcmd.c:407
static bool check_can_decode(struct AttachCtx *actx, struct Body *cur)
Can we decode all tagged attachments?
Definition: recvcmd.c:109
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:44
#define MUTT_ALIAS
Do alias "completion" by calling up the alias-menu.
Definition: mutt.h:53
static void attach_include_reply(FILE *fp, FILE *fp_tmp, struct Email *e)
This is very similar to send.c&#39;s include_reply()
Definition: recvcmd.c:908
struct Body * body
List of MIME parts.
Definition: email.h:91
#define MUTT_CHARCONV
Do character set conversions.
Definition: state.h:36
Structs that make up an email.
String processing routines to generate the mail index.
#define mutt_error(...)
Definition: logging.h:88
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:458
Convenience wrapper for the send headers.
void mutt_forward_intro(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add the "start of forwarded message" text.
Definition: send.c:429
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1470
int mutt_get_field(const char *field, char *buf, size_t buflen, CompletionFlags complete, bool multiple, char ***files, int *numfiles)
Ask the user for a string.
Definition: curs_lib.c:335
void mutt_make_forward_subject(struct Envelope *env, struct Email *e, struct ConfigSubset *sub)
Create a subject for a forwarded email.
Definition: send.c:1034
static bool check_msg(struct Body *b, bool err)
Are we working with an RFC822 message.
Definition: recvcmd.c:67
size_t idx
Definition: mailbox.c:257
NeoMutt Logging.
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
size_t msgwin_get_width(void)
Get the width of the Message Window.
Definition: msgwin.c:262
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:195
FILE * fp_root
Used by recvattach for updating.
Definition: attach.h:52
#define MUTT_CM_WEED
Weed message/rfc822 attachment headers.
Definition: copy.h:41
String manipulation buffer.
Definition: buffer.h:33
char * prefix
String to add to the beginning of each output line.
Definition: state.h:48
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
#define _(a)
Definition: message.h:28
short idxlen
Number of attachmentes.
Definition: attach.h:55
FILE * fp_out
File to write to.
Definition: state.h:47
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition: envelope.c:42
void emaillist_clear(struct EmailList *el)
Drop a private list of Emails.
Definition: email.c:138
uint16_t CopyMessageFlags
Flags for mutt_copy_message(), e.g. MUTT_CM_NOHEADER.
Definition: copy.h:32
uint16_t SendFlags
Flags for mutt_send_message(), e.g. SEND_REPLY.
Definition: send.h:36
Flags to control mutt_expando_format()
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition: copy.h:35
void msgwin_clear_text(void)
Clear the text in the Message Window.
Definition: msgwin.c:242
Container for Accounts, Notifications.
Definition: neomutt.h:36
FILE * fp_in
File to read from.
Definition: state.h:46
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
Messages that have been replied to.
Definition: mutt.h:91
The body of an email.
Definition: body.h:34
QuadOption
Possible values for a quad-option.
Definition: quad.h:35
Convenience wrapper for the config headers.
static void attach_forward_msgs(FILE *fp, struct AttachCtx *actx, struct Body *cur, SendFlags flags)
Forward one or several message-type attachments.
Definition: recvcmd.c:654
Email Address Handling.
struct Email * email_new(void)
Create a new Email.
Definition: email.c:78
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
#define SEND_NO_FLAGS
No flags are set.
Definition: send.h:39
Some miscellaneous functions.
StateFlags flags
Flags, e.g. MUTT_DISPLAY.
Definition: state.h:49
void mutt_make_attribution(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add "on DATE, PERSON wrote" header.
Definition: send.c:618
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:1379
Many unsorted constants and some structs.
struct AddressList from
Email&#39;s &#39;From&#39; list.
Definition: envelope.h:57
void mutt_make_post_indent(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add suffix to replied email text.
Definition: send.c:729
#define CH_WEED
Weed the headers?
Definition: copy.h:53
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
void mutt_make_string(char *buf, size_t buflen, int cols, const char *s, struct Mailbox *m, int inpgr, struct Email *e, MuttFormatFlags flags, const char *progress)
Create formatted strings using mailbox expandos.
Definition: hdrline.c:1409
struct Envelope * env
Envelope information.
Definition: email.h:90
Convenience wrapper for the core headers.
int mutt_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile, struct Mailbox *m, struct EmailList *el, struct ConfigSubset *sub)
Send an email.
Definition: send.c:2125
int mutt_body_copy(FILE *fp, struct Body **tgt, struct Body *src)
Create a send-mode duplicate from a receive-mode body.
Definition: mutt_body.c:48
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: question.c:347
#define SEND_LIST_REPLY
Reply to mailing list.
Definition: send.h:42
bool tagged
This attachment is tagged.
Definition: body.h:70
Email Aliases.
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:112
User aborted the question (with Ctrl-G)
Definition: quad.h:37
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:916
char * subtype
content-type subtype
Definition: body.h:37
static int is_parent(short i, struct AttachCtx *actx, struct Body *cur)
Check whether one attachment is the parent of another.
Definition: recvcmd.c:355
#define MUTT_CM_PREFIX
Quote the header and body.
Definition: copy.h:37
Prototypes for many functions.
void mutt_attach_bounce(struct Mailbox *m, FILE *fp, struct AttachCtx *actx, struct Body *cur)
Bounce function, from the attachment menu.
Definition: recvcmd.c:161
Representation of the body of an email.
static struct AttachPtr * find_parent(struct AttachCtx *actx, struct Body *cur, short nattach)
Find the parent of an Attachment.
Definition: recvcmd.c:376
void mutt_attach_reply(FILE *fp, struct Mailbox *m, struct Email *e, struct AttachCtx *actx, struct Body *e_cur, SendFlags flags)
Attach a reply.
Definition: recvcmd.c:938
A mailbox.
Definition: mailbox.h:81
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:983
void mutt_fix_reply_recipients(struct Envelope *env, struct ConfigSubset *sub)
Remove duplicate recipients.
Definition: send.c:1004
#define MUTT_CM_DECODE
Decode the message body into text/plain.
Definition: copy.h:38
#define CH_REORDER
Re-order output of headers (specified by &#39;hdr_order&#39;)
Definition: copy.h:59
#define MUTT_CM_CHARCONV
Perform character set conversions.
Definition: copy.h:42
bool mutt_can_decode(struct Body *a)
Will decoding the attachment produce any output.
Definition: handler.c:1821
static struct AttachPtr * find_common_parent(struct AttachCtx *actx, short nattach)
find a common parent message for the tagged attachments
Definition: recvcmd.c:321
#define CH_DECODE
Do RFC2047 header decoding.
Definition: copy.h:54
#define CH_XMIT
Transmitting this message? (Ignore Lines: and Content-Length:)
Definition: copy.h:55
#define MUTT_WEED
Weed headers even when not in display mode.
Definition: state.h:35
FILE * fp
Used in the recvattach menu.
Definition: attach.h:37
static short count_tagged(struct AttachCtx *actx)
Count the number of tagged attachments.
Definition: recvcmd.c:126
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
Ask the user a question.
#define SEND_NEWS
Reply to a news article.
Definition: send.h:53
int mutt_fetch_recips(struct Envelope *out, struct Envelope *in, SendFlags flags, struct ConfigSubset *sub)
Generate recpients for a reply email.
Definition: send.c:915
int emaillist_add_email(struct EmailList *el, struct Email *e)
Add an Email to a list.
Definition: email.c:159
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition: helpers.c:218
unsigned int type
content-type primary type, ContentType
Definition: body.h:65
void mutt_attach_resend(FILE *fp, struct Mailbox *m, struct AttachCtx *actx, struct Body *cur)
resend-message, from the attachment menu
Definition: recvcmd.c:294
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:385
void mutt_attach_forward(FILE *fp, struct Email *e, struct AttachCtx *actx, struct Body *cur, SendFlags flags)
Forward an Attachment.
Definition: recvcmd.c:784
static struct Body ** copy_problematic_attachments(struct Body **last, struct AttachCtx *actx, bool force)
Attach the body parts which can&#39;t be decoded.
Definition: recvcmd.c:446
Duplicate the structure of an entire email.
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition: address.c:1305
Send/reply with an attachment.
char * newsgroups
List of newsgroups.
Definition: envelope.h:75
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:749
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition: copy.h: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.c:413
void mutt_forward_trailer(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add a "end of forwarded message" text.
Definition: send.c:454
char * followup_to
List of &#39;followup-to&#39; fields.
Definition: envelope.h:77
#define EXTRA_SPACE
#define mutt_message(...)
Definition: logging.h:87
#define FREE(x)
Definition: memory.h:40
Keep track when processing files.
Definition: state.h:44
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
void mutt_attach_mail_sender(FILE *fp, struct Email *e, struct AttachCtx *actx, struct Body *cur)
Compose an email to the sender in the email attachment.
Definition: recvcmd.c:1115
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:38
struct AddressList to
Email&#39;s &#39;To&#39; list.
Definition: envelope.h:58
int mutt_body_handler(struct Body *b, struct State *s)
Handler for the Body of an email.
Definition: handler.c:1604
void mutt_make_misc_reply_headers(struct Envelope *env, struct Envelope *curenv, struct ConfigSubset *sub)
Set subject for a reply.
Definition: send.c:1054
Handling of global boolean variables.
#define state_putc(STATE, STR)
Definition: state.h:56
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
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:815
int mutt_resend_message(FILE *fp, struct Mailbox *m, struct Email *e_cur, struct ConfigSubset *sub)
Resend an email.
Definition: send.c:1632
#define TAILQ_EMPTY(head)
Definition: queue.h:721
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:629
#define MUTT_CM_NOHEADER
Don&#39;t copy the message header.
Definition: copy.h:36
WHERE bool OptNewsSend
(pseudo) used to change behavior when posting
Definition: options.h:45
Convenience wrapper for the library headers.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:637
Decide how to display email content.
struct Email * email
header information for message/rfc822
Definition: body.h:55
A set of attachments.
Definition: attach.h:49
void mutt_add_to_reference_headers(struct Envelope *env, struct Envelope *curenv, struct ConfigSubset *sub)
Generate references for a reply email.
Definition: send.c:1081
int level
Nesting depth of attachment.
Definition: attach.h:40
#define CH_PREFIX
Quote header using $indent_string string?
Definition: copy.h:57
static short count_tagged_children(struct AttachCtx *actx, short i)
tagged children below a multipart/message attachment
Definition: recvcmd.c:142
struct AttachPtr ** idx
Array of attachments.
Definition: attach.h:54
size_t mutt_addrlist_write(const struct AddressList *al, char *buf, size_t buflen, bool display)
Write an Address to a buffer.
Definition: address.c:1150
The header of an Email.
Definition: envelope.h:54
void mutt_simple_format(char *buf, size_t buflen, int min_width, int max_width, enum FormatJustify justify, char pad_char, const char *s, size_t n, bool arboreal)
Format a string, like snprintf()
Definition: curs_lib.c:716
static void attach_forward_bodies(FILE *fp, struct Email *e, struct AttachCtx *actx, struct Body *cur, short nattach)
forward one or several MIME bodies
Definition: recvcmd.c:471
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:39
struct Body * body
Attachment.
Definition: attach.h:36