NeoMutt  2021-02-05
Teaching an old dog new tricks
DOXYGEN
recvattach.c
Go to the documentation of this file.
1 
30 #include "config.h"
31 #include <limits.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37 #include "mutt/lib.h"
38 #include "config/lib.h"
39 #include "email/lib.h"
40 #include "core/lib.h"
41 #include "gui/lib.h"
42 #include "mutt.h"
43 #include "recvattach.h"
44 #include "ncrypt/lib.h"
45 #include "nntp/lib.h"
46 #include "send/lib.h"
47 #include "commands.h"
48 #include "context.h"
49 #include "format_flags.h"
50 #include "handler.h"
51 #include "hdrline.h"
52 #include "hook.h"
53 #include "keymap.h"
54 #include "mailcap.h"
55 #include "mutt_attach.h"
56 #include "mutt_globals.h"
57 #include "mutt_menu.h"
58 #include "mutt_parse.h"
59 #include "mutt_thread.h"
60 #include "muttlib.h"
61 #include "mx.h"
62 #include "opcodes.h"
63 #include "options.h"
64 #include "recvcmd.h"
65 #include "rfc3676.h"
66 #include "state.h"
67 #ifdef ENABLE_NLS
68 #include <libintl.h>
69 #endif
70 
71 /* These Config Variables are only used in recvattach.c */
74 char *C_AttachSep;
78 
79 static void mutt_update_recvattach_menu(struct AttachCtx *actx, struct Menu *menu, bool init);
80 
81 static const char *Mailbox_is_read_only = N_("Mailbox is read-only");
82 
83 #define CHECK_READONLY \
84  if (!Context || !Context->mailbox || Context->mailbox->readonly) \
85  { \
86  mutt_flushinp(); \
87  mutt_error(_(Mailbox_is_read_only)); \
88  break; \
89  }
90 
91 #define CUR_ATTACH actx->idx[actx->v2r[menu->current]]
92 
94 static const struct Mapping AttachHelp[] = {
95  // clang-format off
96  { N_("Exit"), OP_EXIT },
97  { N_("Save"), OP_SAVE },
98  { N_("Pipe"), OP_PIPE },
99  { N_("Print"), OP_PRINT },
100  { N_("Help"), OP_HELP },
101  { NULL, 0 },
102  // clang-format on
103 };
104 
105 static const char *Function_not_permitted =
106  N_("Function not permitted in attach-message mode");
107 
108 #define CHECK_ATTACH \
109  if (OptAttachMsg) \
110  { \
111  mutt_flushinp(); \
112  mutt_error(_(Function_not_permitted)); \
113  break; \
114  }
115 
122 static void mutt_update_v2r(struct AttachCtx *actx)
123 {
124  int vindex, rindex, curlevel;
125 
126  vindex = 0;
127  rindex = 0;
128 
129  while (rindex < actx->idxlen)
130  {
131  actx->v2r[vindex++] = rindex;
132  if (actx->idx[rindex]->body->collapsed)
133  {
134  curlevel = actx->idx[rindex]->level;
135  do
136  {
137  rindex++;
138  } while ((rindex < actx->idxlen) && (actx->idx[rindex]->level > curlevel));
139  }
140  else
141  rindex++;
142  }
143 
144  actx->vcount = vindex;
145 }
146 
151 void mutt_update_tree(struct AttachCtx *actx)
152 {
153  char buf[256];
154  char *s = NULL;
155 
156  mutt_update_v2r(actx);
157 
158  for (int vindex = 0; vindex < actx->vcount; vindex++)
159  {
160  const int rindex = actx->v2r[vindex];
161  actx->idx[rindex]->num = vindex;
162  if ((2 * (actx->idx[rindex]->level + 2)) < sizeof(buf))
163  {
164  if (actx->idx[rindex]->level)
165  {
166  s = buf + 2 * (actx->idx[rindex]->level - 1);
167  *s++ = (actx->idx[rindex]->body->next) ? MUTT_TREE_LTEE : MUTT_TREE_LLCORNER;
168  *s++ = MUTT_TREE_HLINE;
169  *s++ = MUTT_TREE_RARROW;
170  }
171  else
172  s = buf;
173  *s = '\0';
174  }
175 
176  if (actx->idx[rindex]->tree)
177  {
178  if (!mutt_str_equal(actx->idx[rindex]->tree, buf))
179  mutt_str_replace(&actx->idx[rindex]->tree, buf);
180  }
181  else
182  actx->idx[rindex]->tree = mutt_str_dup(buf);
183 
184  if (((2 * (actx->idx[rindex]->level + 2)) < sizeof(buf)) &&
185  actx->idx[rindex]->level)
186  {
187  s = buf + 2 * (actx->idx[rindex]->level - 1);
188  *s++ = (actx->idx[rindex]->body->next) ? '\005' : '\006';
189  *s++ = '\006';
190  }
191  }
192 }
193 
217 const char *attach_format_str(char *buf, size_t buflen, size_t col, int cols, char op,
218  const char *src, const char *prec, const char *if_str,
219  const char *else_str, intptr_t data, MuttFormatFlags flags)
220 {
221  char fmt[128];
222  char charset[128];
223  struct AttachPtr *aptr = (struct AttachPtr *) data;
224  bool optional = (flags & MUTT_FORMAT_OPTIONAL);
225 
226  switch (op)
227  {
228  case 'C':
229  if (!optional)
230  {
231  if (mutt_is_text_part(aptr->body) &&
232  mutt_body_get_charset(aptr->body, charset, sizeof(charset)))
233  {
234  mutt_format_s(buf, buflen, prec, charset);
235  }
236  else
237  mutt_format_s(buf, buflen, prec, "");
238  }
239  else if (!mutt_is_text_part(aptr->body) ||
240  !mutt_body_get_charset(aptr->body, charset, sizeof(charset)))
241  {
242  optional = false;
243  }
244  break;
245  case 'c':
246  /* XXX */
247  if (!optional)
248  {
249  snprintf(fmt, sizeof(fmt), "%%%sc", prec);
250  snprintf(buf, buflen, fmt,
251  ((aptr->body->type != TYPE_TEXT) || aptr->body->noconv) ? 'n' : 'c');
252  }
253  else if ((aptr->body->type != TYPE_TEXT) || aptr->body->noconv)
254  optional = false;
255  break;
256  case 'd':
257  if (!optional)
258  {
259  if (aptr->body->description)
260  {
261  mutt_format_s(buf, buflen, prec, aptr->body->description);
262  break;
263  }
264  if (mutt_is_message_type(aptr->body->type, aptr->body->subtype) &&
265  C_MessageFormat && aptr->body->email)
266  {
267  char s[128];
268  mutt_make_string_flags(s, sizeof(s), cols, C_MessageFormat, NULL, -1,
269  aptr->body->email,
271  if (*s)
272  {
273  mutt_format_s(buf, buflen, prec, s);
274  break;
275  }
276  }
277  if (!aptr->body->d_filename && !aptr->body->filename)
278  {
279  mutt_format_s(buf, buflen, prec, "<no description>");
280  break;
281  }
282  }
283  else if (aptr->body->description ||
284  (mutt_is_message_type(aptr->body->type, aptr->body->subtype) &&
285  C_MessageFormat && aptr->body->email))
286  {
287  break;
288  }
289  /* fallthrough */
290  case 'F':
291  if (!optional)
292  {
293  if (aptr->body->d_filename)
294  {
295  mutt_format_s(buf, buflen, prec, aptr->body->d_filename);
296  break;
297  }
298  }
299  else if (!aptr->body->d_filename && !aptr->body->filename)
300  {
301  optional = false;
302  break;
303  }
304  /* fallthrough */
305  case 'f':
306  if (!optional)
307  {
308  if (aptr->body->filename && (*aptr->body->filename == '/'))
309  {
310  struct Buffer *path = mutt_buffer_pool_get();
311 
312  mutt_buffer_strcpy(path, aptr->body->filename);
314  mutt_format_s(buf, buflen, prec, mutt_buffer_string(path));
316  }
317  else
318  mutt_format_s(buf, buflen, prec, NONULL(aptr->body->filename));
319  }
320  else if (!aptr->body->filename)
321  optional = false;
322  break;
323  case 'D':
324  if (!optional)
325  snprintf(buf, buflen, "%c", aptr->body->deleted ? 'D' : ' ');
326  else if (!aptr->body->deleted)
327  optional = false;
328  break;
329  case 'e':
330  if (!optional)
331  mutt_format_s(buf, buflen, prec, ENCODING(aptr->body->encoding));
332  break;
333  case 'I':
334  if (optional)
335  break;
336 
337  const char dispchar[] = { 'I', 'A', 'F', '-' };
338  char ch;
339 
340  if (aptr->body->disposition < sizeof(dispchar))
341  ch = dispchar[aptr->body->disposition];
342  else
343  {
344  mutt_debug(LL_DEBUG1, "ERROR: invalid content-disposition %d\n",
345  aptr->body->disposition);
346  ch = '!';
347  }
348  snprintf(buf, buflen, "%c", ch);
349  break;
350  case 'm':
351  if (!optional)
352  mutt_format_s(buf, buflen, prec, TYPE(aptr->body));
353  break;
354  case 'M':
355  if (!optional)
356  mutt_format_s(buf, buflen, prec, aptr->body->subtype);
357  else if (!aptr->body->subtype)
358  optional = false;
359  break;
360  case 'n':
361  if (optional)
362  break;
363 
364  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
365  snprintf(buf, buflen, fmt, aptr->num + 1);
366  break;
367  case 'Q':
368  if (optional)
369  optional = aptr->body->attach_qualifies;
370  else
371  {
372  snprintf(fmt, sizeof(fmt), "%%%sc", prec);
373  mutt_format_s(buf, buflen, fmt, "Q");
374  }
375  break;
376  case 's':
377  {
378  size_t l;
379  if (aptr->body->filename && (flags & MUTT_FORMAT_STAT_FILE))
380  {
381  struct stat st;
382  stat(aptr->body->filename, &st);
383  l = st.st_size;
384  }
385  else
386  l = aptr->body->length;
387 
388  if (!optional)
389  {
390  char tmp[128];
391  mutt_str_pretty_size(tmp, sizeof(tmp), l);
392  mutt_format_s(buf, buflen, prec, tmp);
393  }
394  else if (l == 0)
395  optional = false;
396 
397  break;
398  }
399  case 't':
400  if (!optional)
401  snprintf(buf, buflen, "%c", aptr->body->tagged ? '*' : ' ');
402  else if (!aptr->body->tagged)
403  optional = false;
404  break;
405  case 'T':
406  if (!optional)
407  mutt_format_s_tree(buf, buflen, prec, NONULL(aptr->tree));
408  else if (!aptr->tree)
409  optional = false;
410  break;
411  case 'u':
412  if (!optional)
413  snprintf(buf, buflen, "%c", aptr->body->unlink ? '-' : ' ');
414  else if (!aptr->body->unlink)
415  optional = false;
416  break;
417  case 'X':
418  if (optional)
419  optional = ((aptr->body->attach_count + aptr->body->attach_qualifies) != 0);
420  else
421  {
422  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
423  snprintf(buf, buflen, fmt, aptr->body->attach_count + aptr->body->attach_qualifies);
424  }
425  break;
426  default:
427  *buf = '\0';
428  }
429 
430  if (optional)
431  {
432  mutt_expando_format(buf, buflen, col, cols, if_str, attach_format_str, data,
434  }
435  else if (flags & MUTT_FORMAT_OPTIONAL)
436  {
437  mutt_expando_format(buf, buflen, col, cols, else_str, attach_format_str,
438  data, MUTT_FORMAT_NO_FLAGS);
439  }
440  return src;
441 }
442 
446 static void attach_make_entry(char *buf, size_t buflen, struct Menu *menu, int line)
447 {
448  struct AttachCtx *actx = menu->mdata;
449 
450  mutt_expando_format(buf, buflen, 0, menu->win_index->state.cols,
452  (intptr_t)(actx->idx[actx->v2r[line]]), MUTT_FORMAT_ARROWCURSOR);
453 }
454 
458 int attach_tag(struct Menu *menu, int sel, int act)
459 {
460  struct AttachCtx *actx = menu->mdata;
461  struct Body *cur = actx->idx[actx->v2r[sel]]->body;
462  bool ot = cur->tagged;
463 
464  cur->tagged = ((act >= 0) ? act : !cur->tagged);
465  return cur->tagged - ot;
466 }
467 
472 static void prepend_savedir(struct Buffer *buf)
473 {
474  if (!buf || !buf->data || (buf->data[0] == '/'))
475  return;
476 
477  struct Buffer *tmp = mutt_buffer_pool_get();
478  if (C_AttachSaveDir)
479  {
481  if (tmp->dptr[-1] != '/')
482  mutt_buffer_addch(tmp, '/');
483  }
484  else
485  mutt_buffer_addstr(tmp, "./");
486 
488  mutt_buffer_copy(buf, tmp);
490 }
491 
497 static bool has_a_message(struct Body *body)
498 {
499  return (body->email && (body->encoding != ENC_BASE64) &&
500  (body->encoding != ENC_QUOTED_PRINTABLE) &&
501  mutt_is_message_type(body->type, body->subtype));
502 }
503 
529 static int save_attachment_flowed_helper(FILE *fp, struct Body *b, const char *path,
530  enum SaveAttach flags, struct Email *e)
531 {
532  int rc = -1;
533 
535  {
536  struct Body b_fake = { 0 };
537 
538  struct Buffer *tempfile = mutt_buffer_pool_get();
539  mutt_buffer_mktemp(tempfile);
540 
541  /* Pass MUTT_SAVE_NO_FLAGS to force mutt_file_fopen("w") */
543  if (rc != 0)
544  goto cleanup;
545 
547 
548  /* Now "really" save it. Send mode does this without touching anything,
549  * so force send-mode. */
550  memset(&b_fake, 0, sizeof(struct Body));
551  b_fake.filename = tempfile->data;
552  rc = mutt_save_attachment(NULL, &b_fake, path, flags, e);
553 
555 
556  cleanup:
557  mutt_buffer_pool_release(&tempfile);
558  }
559  else
560  {
561  rc = mutt_save_attachment(fp, b, path, flags, e);
562  }
563 
564  return rc;
565 }
566 
576 static int query_save_attachment(FILE *fp, struct Body *body, struct Email *e, char **directory)
577 {
578  char *prompt = NULL;
579  enum SaveAttach opt = MUTT_SAVE_NO_FLAGS;
580  int rc = -1;
581 
582  struct Buffer *buf = mutt_buffer_pool_get();
583  struct Buffer *tfile = mutt_buffer_pool_get();
584 
585  if (body->filename)
586  {
587  if (directory && *directory)
588  {
589  mutt_buffer_concat_path(buf, *directory, mutt_path_basename(body->filename));
590  }
591  else
592  mutt_buffer_strcpy(buf, body->filename);
593  }
594  else if (has_a_message(body))
595  {
596  mutt_default_save(buf->data, buf->dsize, body->email);
598  }
599 
600  prepend_savedir(buf);
601 
602  prompt = _("Save to file: ");
603  while (prompt)
604  {
605  if ((mutt_buffer_get_field(prompt, buf, MUTT_FILE | MUTT_CLEAR) != 0) ||
607  {
608  goto cleanup;
609  }
610 
611  prompt = NULL;
613 
614  bool is_message = (fp && has_a_message(body));
615 
616  if (is_message)
617  {
618  struct stat st;
619 
620  /* check to make sure that this file is really the one the user wants */
621  rc = mutt_save_confirm(mutt_buffer_string(buf), &st);
622  if (rc == 1)
623  {
624  prompt = _("Save to file: ");
625  continue;
626  }
627  else if (rc == -1)
628  goto cleanup;
629  mutt_buffer_copy(tfile, buf);
630  }
631  else
632  {
633  rc = mutt_check_overwrite(body->filename, mutt_buffer_string(buf), tfile,
634  &opt, directory);
635  if (rc == -1)
636  goto cleanup;
637  else if (rc == 1)
638  {
639  prompt = _("Save to file: ");
640  continue;
641  }
642  }
643 
644  mutt_message(_("Saving..."));
645  if (save_attachment_flowed_helper(fp, body, mutt_buffer_string(tfile), opt,
646  (e || !is_message) ? e : body->email) == 0)
647  {
648  mutt_message(_("Attachment saved"));
649  rc = 0;
650  goto cleanup;
651  }
652  else
653  {
654  prompt = _("Save to file: ");
655  continue;
656  }
657  }
658 
659 cleanup:
661  mutt_buffer_pool_release(&tfile);
662  return rc;
663 }
664 
673 static int save_without_prompting(FILE *fp, struct Body *body, struct Email *e)
674 {
675  enum SaveAttach opt = MUTT_SAVE_NO_FLAGS;
676  int rc = -1;
677  struct Buffer *buf = mutt_buffer_pool_get();
678  struct Buffer *tfile = mutt_buffer_pool_get();
679 
680  if (body->filename)
681  {
682  mutt_buffer_strcpy(buf, body->filename);
683  }
684  else if (has_a_message(body))
685  {
686  mutt_default_save(buf->data, buf->dsize, body->email);
687  }
688 
689  prepend_savedir(buf);
691 
692  bool is_message = (fp && has_a_message(body));
693 
694  if (is_message)
695  {
696  mutt_buffer_copy(tfile, buf);
697  }
698  else
699  {
700  rc = mutt_check_overwrite(body->filename, mutt_buffer_string(buf), tfile, &opt, NULL);
701  if (rc == -1) // abort or cancel
702  goto cleanup;
703  }
704 
705  rc = save_attachment_flowed_helper(fp, body, mutt_buffer_string(tfile), opt,
706  (e || !is_message) ? e : body->email);
707 
708 cleanup:
710  mutt_buffer_pool_release(&tfile);
711  return rc;
712 }
713 
723 void mutt_save_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag,
724  struct Body *top, struct Email *e, struct Menu *menu)
725 {
726  char *directory = NULL;
727  int rc = 1;
728  int last = menu ? menu->current : -1;
729  FILE *fp_out = NULL;
730  int saved_attachments = 0;
731 
732  struct Buffer *buf = mutt_buffer_pool_get();
733  struct Buffer *tfile = mutt_buffer_pool_get();
734 
735  for (int i = 0; !tag || (i < actx->idxlen); i++)
736  {
737  if (tag)
738  {
739  fp = actx->idx[i]->fp;
740  top = actx->idx[i]->body;
741  }
742  if (!tag || top->tagged)
743  {
744  if (!C_AttachSplit)
745  {
746  if (mutt_buffer_is_empty(buf))
747  {
748  enum SaveAttach opt = MUTT_SAVE_NO_FLAGS;
749 
751  prepend_savedir(buf);
752 
753  if ((mutt_buffer_get_field(_("Save to file: "), buf, MUTT_FILE | MUTT_CLEAR) != 0) ||
755  {
756  goto cleanup;
757  }
759  if (mutt_check_overwrite(top->filename, mutt_buffer_string(buf), tfile, &opt, NULL))
760  goto cleanup;
761  rc = save_attachment_flowed_helper(fp, top, mutt_buffer_string(tfile), opt, e);
762  if ((rc == 0) && C_AttachSep && (fp_out = fopen(mutt_buffer_string(tfile), "a")))
763  {
764  fprintf(fp_out, "%s", C_AttachSep);
765  mutt_file_fclose(&fp_out);
766  }
767  }
768  else
769  {
771  MUTT_SAVE_APPEND, e);
772  if ((rc == 0) && C_AttachSep && (fp_out = fopen(mutt_buffer_string(tfile), "a")))
773  {
774  fprintf(fp_out, "%s", C_AttachSep);
775  mutt_file_fclose(&fp_out);
776  }
777  }
778  }
779  else
780  {
781  if (tag && menu && top->aptr)
782  {
783  menu->oldcurrent = menu->current;
784  menu->current = top->aptr->num;
785  menu_check_recenter(menu);
786  menu->redraw |= REDRAW_MOTION;
787 
788  menu_redraw(menu);
789  }
791  {
792  // Save each file, with no prompting, using the configured 'AttachSaveDir'
793  rc = save_without_prompting(fp, top, e);
794  if (rc == 0)
795  saved_attachments++;
796  }
797  else
798  {
799  // Save each file, prompting the user for the location each time.
800  if (query_save_attachment(fp, top, e, &directory) == -1)
801  break;
802  }
803  }
804  }
805  if (!tag)
806  break;
807  }
808 
809  FREE(&directory);
810 
811  if (tag && menu)
812  {
813  menu->oldcurrent = menu->current;
814  menu->current = last;
815  menu_check_recenter(menu);
816  menu->redraw |= REDRAW_MOTION;
817  }
818 
819  if (!C_AttachSplit && (rc == 0))
820  mutt_message(_("Attachment saved"));
821 
822  if (C_AttachSaveWithoutPrompting && (rc == 0))
823  {
824  mutt_message(ngettext("Attachment saved", "%d attachments saved", saved_attachments),
825  saved_attachments);
826  }
827 
828 cleanup:
830  mutt_buffer_pool_release(&tfile);
831 }
832 
840 static void query_pipe_attachment(const char *command, FILE *fp, struct Body *body, bool filter)
841 {
842  char tfile[PATH_MAX];
843 
844  if (filter)
845  {
846  char warning[PATH_MAX + 256];
847  snprintf(warning, sizeof(warning),
848  _("WARNING! You are about to overwrite %s, continue?"), body->filename);
849  if (mutt_yesorno(warning, MUTT_NO) != MUTT_YES)
850  {
852  return;
853  }
854  mutt_mktemp(tfile, sizeof(tfile));
855  }
856  else
857  tfile[0] = '\0';
858 
859  if (mutt_pipe_attachment(fp, body, command, tfile))
860  {
861  if (filter)
862  {
863  mutt_file_unlink(body->filename);
864  mutt_file_rename(tfile, body->filename);
866  mutt_message(_("Attachment filtered"));
867  }
868  }
869  else
870  {
871  if (filter && tfile[0])
872  mutt_file_unlink(tfile);
873  }
874 }
875 
882 static void pipe_attachment(FILE *fp, struct Body *b, struct State *state)
883 {
884  if (!state || !state->fp_out)
885  return;
886 
887  FILE *fp_in = NULL;
888  FILE *fp_unstuff = NULL;
889  bool is_flowed = false, unlink_unstuff = false;
890  struct Buffer *unstuff_tempfile = NULL;
891 
893  {
894  is_flowed = true;
895  unstuff_tempfile = mutt_buffer_pool_get();
896  mutt_buffer_mktemp(unstuff_tempfile);
897  }
898 
899  if (fp)
900  {
901  state->fp_in = fp;
902 
903  if (is_flowed)
904  {
905  fp_unstuff = mutt_file_fopen(mutt_buffer_string(unstuff_tempfile), "w");
906  if (fp_unstuff == NULL)
907  {
908  mutt_perror("mutt_file_fopen");
909  goto bail;
910  }
911  unlink_unstuff = true;
912 
913  FILE *filter_fp = state->fp_out;
914  state->fp_out = fp_unstuff;
915  mutt_decode_attachment(b, state);
916  mutt_file_fclose(&fp_unstuff);
917  state->fp_out = filter_fp;
918 
919  fp_unstuff = mutt_file_fopen(mutt_buffer_string(unstuff_tempfile), "r");
920  if (fp_unstuff == NULL)
921  {
922  mutt_perror("mutt_file_fopen");
923  goto bail;
924  }
925  mutt_file_copy_stream(fp_unstuff, filter_fp);
926  mutt_file_fclose(&fp_unstuff);
927  }
928  else
929  mutt_decode_attachment(b, state);
930  }
931  else
932  {
933  const char *infile = NULL;
934 
935  if (is_flowed)
936  {
937  if (mutt_save_attachment(fp, b, mutt_buffer_string(unstuff_tempfile), 0, NULL) == -1)
938  goto bail;
939  unlink_unstuff = true;
941  infile = mutt_buffer_string(unstuff_tempfile);
942  }
943  else
944  infile = b->filename;
945 
946  fp_in = fopen(infile, "r");
947  if (!fp_in)
948  {
949  mutt_perror("fopen");
950  goto bail;
951  }
952  mutt_file_copy_stream(fp_in, state->fp_out);
953  mutt_file_fclose(&fp_in);
954  }
955 
956  if (C_AttachSep)
957  state_puts(state, C_AttachSep);
958 
959 bail:
960  mutt_file_fclose(&fp_unstuff);
961  mutt_file_fclose(&fp_in);
962 
963  if (unlink_unstuff)
964  mutt_file_unlink(mutt_buffer_string(unstuff_tempfile));
965  mutt_buffer_pool_release(&unstuff_tempfile);
966 }
967 
978 static void pipe_attachment_list(const char *command, struct AttachCtx *actx,
979  FILE *fp, bool tag, struct Body *top,
980  bool filter, struct State *state)
981 {
982  for (int i = 0; !tag || (i < actx->idxlen); i++)
983  {
984  if (tag)
985  {
986  fp = actx->idx[i]->fp;
987  top = actx->idx[i]->body;
988  }
989  if (!tag || top->tagged)
990  {
991  if (!filter && !C_AttachSplit)
992  pipe_attachment(fp, top, state);
993  else
994  query_pipe_attachment(command, fp, top, filter);
995  }
996  if (!tag)
997  break;
998  }
999 }
1000 
1009 void mutt_pipe_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag,
1010  struct Body *top, bool filter)
1011 {
1012  struct State state = { 0 };
1013  struct Buffer *buf = NULL;
1014 
1015  if (fp)
1016  filter = false; /* sanity check: we can't filter in the recv case yet */
1017 
1018  buf = mutt_buffer_pool_get();
1019  /* perform charset conversion on text attachments when piping */
1020  state.flags = MUTT_CHARCONV;
1021 
1022  if (mutt_buffer_get_field((filter ? _("Filter through: ") : _("Pipe to: ")),
1023  buf, MUTT_CMD) != 0)
1024  {
1025  goto cleanup;
1026  }
1027 
1028  if (mutt_buffer_len(buf) == 0)
1029  goto cleanup;
1030 
1032 
1033  if (!filter && !C_AttachSplit)
1034  {
1035  mutt_endwin();
1036  pid_t pid = filter_create(mutt_buffer_string(buf), &state.fp_out, NULL, NULL);
1037  pipe_attachment_list(mutt_buffer_string(buf), actx, fp, tag, top, filter, &state);
1038  mutt_file_fclose(&state.fp_out);
1039  if ((filter_wait(pid) != 0) || C_WaitKey)
1041  }
1042  else
1043  pipe_attachment_list(mutt_buffer_string(buf), actx, fp, tag, top, filter, &state);
1044 
1045 cleanup:
1047 }
1048 
1056 static bool can_print(struct AttachCtx *actx, struct Body *top, bool tag)
1057 {
1058  char type[256];
1059 
1060  for (int i = 0; !tag || (i < actx->idxlen); i++)
1061  {
1062  if (tag)
1063  top = actx->idx[i]->body;
1064  snprintf(type, sizeof(type), "%s/%s", TYPE(top), top->subtype);
1065  if (!tag || top->tagged)
1066  {
1067  if (!mailcap_lookup(top, type, sizeof(type), NULL, MUTT_MC_PRINT))
1068  {
1069  if (!mutt_istr_equal("text/plain", top->subtype) &&
1070  !mutt_istr_equal("application/postscript", top->subtype))
1071  {
1072  if (!mutt_can_decode(top))
1073  {
1074  /* L10N: s gets replaced by a MIME type, e.g. "text/plain" or
1075  application/octet-stream. */
1076  mutt_error(_("I don't know how to print %s attachments"), type);
1077  return false;
1078  }
1079  }
1080  }
1081  }
1082  if (!tag)
1083  break;
1084  }
1085  return true;
1086 }
1087 
1096 static void print_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag,
1097  struct Body *top, struct State *state)
1098 {
1099  char type[256];
1100 
1101  for (int i = 0; !tag || (i < actx->idxlen); i++)
1102  {
1103  if (tag)
1104  {
1105  fp = actx->idx[i]->fp;
1106  top = actx->idx[i]->body;
1107  }
1108  if (!tag || top->tagged)
1109  {
1110  snprintf(type, sizeof(type), "%s/%s", TYPE(top), top->subtype);
1111  if (!C_AttachSplit && !mailcap_lookup(top, type, sizeof(type), NULL, MUTT_MC_PRINT))
1112  {
1113  if (mutt_istr_equal("text/plain", top->subtype) ||
1114  mutt_istr_equal("application/postscript", top->subtype))
1115  {
1116  pipe_attachment(fp, top, state);
1117  }
1118  else if (mutt_can_decode(top))
1119  {
1120  /* decode and print */
1121 
1122  FILE *fp_in = NULL;
1123  struct Buffer *newfile = mutt_buffer_pool_get();
1124 
1125  mutt_buffer_mktemp(newfile);
1126  if (mutt_decode_save_attachment(fp, top, mutt_buffer_string(newfile),
1128  {
1129  if (!state->fp_out)
1130  {
1131  mutt_error(
1132  "BUG in print_attachment_list(). Please report this. ");
1133  return;
1134  }
1135 
1136  fp_in = fopen(mutt_buffer_string(newfile), "r");
1137  if (fp_in)
1138  {
1139  mutt_file_copy_stream(fp_in, state->fp_out);
1140  mutt_file_fclose(&fp_in);
1141  if (C_AttachSep)
1142  state_puts(state, C_AttachSep);
1143  }
1144  }
1146  mutt_buffer_pool_release(&newfile);
1147  }
1148  }
1149  else
1150  mutt_print_attachment(fp, top);
1151  }
1152  if (!tag)
1153  break;
1154  }
1155 }
1156 
1164 void mutt_print_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top)
1165 {
1166  char prompt[128];
1167  struct State state = { 0 };
1168  int tagmsgcount = 0;
1169 
1170  if (tag)
1171  for (int i = 0; i < actx->idxlen; i++)
1172  if (actx->idx[i]->body->tagged)
1173  tagmsgcount++;
1174 
1175  snprintf(prompt, sizeof(prompt),
1176  /* L10N: Although we now the precise number of tagged messages, we
1177  do not show it to the user. So feel free to use a "generic
1178  plural" as plural translation if your language has one. */
1179  tag ? ngettext("Print tagged attachment?", "Print %d tagged attachments?", tagmsgcount) :
1180  _("Print attachment?"),
1181  tagmsgcount);
1182  if (query_quadoption(C_Print, prompt) != MUTT_YES)
1183  return;
1184 
1185  if (C_AttachSplit)
1186  {
1187  print_attachment_list(actx, fp, tag, top, &state);
1188  }
1189  else
1190  {
1191  if (!can_print(actx, top, tag))
1192  return;
1193  mutt_endwin();
1194  pid_t pid = filter_create(NONULL(C_PrintCommand), &state.fp_out, NULL, NULL);
1195  print_attachment_list(actx, fp, tag, top, &state);
1196  mutt_file_fclose(&state.fp_out);
1197  if ((filter_wait(pid) != 0) || C_WaitKey)
1199  }
1200 }
1201 
1207 static void recvattach_extract_pgp_keys(struct AttachCtx *actx, struct Menu *menu)
1208 {
1209  if (!menu->tagprefix)
1211  else
1212  {
1213  for (int i = 0; i < actx->idxlen; i++)
1214  {
1215  if (actx->idx[i]->body->tagged)
1216  {
1217  crypt_pgp_extract_key_from_attachment(actx->idx[i]->fp, actx->idx[i]->body);
1218  }
1219  }
1220  }
1221 }
1222 
1231 static int recvattach_pgp_check_traditional(struct AttachCtx *actx, struct Menu *menu)
1232 {
1233  int rc = 0;
1234 
1235  if (!menu->tagprefix)
1236  rc = crypt_pgp_check_traditional(CUR_ATTACH->fp, CUR_ATTACH->body, true);
1237  else
1238  {
1239  for (int i = 0; i < actx->idxlen; i++)
1240  if (actx->idx[i]->body->tagged)
1241  rc = rc || crypt_pgp_check_traditional(actx->idx[i]->fp, actx->idx[i]->body, true);
1242  }
1243 
1244  return rc;
1245 }
1246 
1253 static void recvattach_edit_content_type(struct AttachCtx *actx,
1254  struct Menu *menu, struct Email *e)
1255 {
1256  if (!mutt_edit_content_type(e, CUR_ATTACH->body, CUR_ATTACH->fp))
1257  return;
1258 
1259  /* The mutt_update_recvattach_menu() will overwrite any changes
1260  * made to a decrypted CUR_ATTACH->body, so warn the user. */
1261  if (CUR_ATTACH->decrypted)
1262  {
1263  mutt_message(
1264  _("Structural changes to decrypted attachments are not supported"));
1265  mutt_sleep(1);
1266  }
1267  /* Editing the content type can rewrite the body structure. */
1268  for (int i = 0; i < actx->idxlen; i++)
1269  actx->idx[i]->body = NULL;
1270  mutt_actx_entries_free(actx);
1271  mutt_update_recvattach_menu(actx, menu, true);
1272 }
1273 
1283 int mutt_attach_display_loop(struct Menu *menu, int op, struct Email *e,
1284  struct AttachCtx *actx, bool recv)
1285 {
1286  do
1287  {
1288  switch (op)
1289  {
1290  case OP_DISPLAY_HEADERS:
1291  bool_str_toggle(NeoMutt->sub, "weed", NULL);
1292  /* fallthrough */
1293 
1294  case OP_VIEW_ATTACH:
1295  op = mutt_view_attachment(CUR_ATTACH->fp, CUR_ATTACH->body,
1296  MUTT_VA_REGULAR, e, actx, menu->win_index);
1297  break;
1298 
1299  case OP_NEXT_ENTRY:
1300  case OP_MAIN_NEXT_UNDELETED: /* hack */
1301  if (menu->current < menu->max - 1)
1302  {
1303  menu->current++;
1304  op = OP_VIEW_ATTACH;
1305  }
1306  else
1307  op = OP_NULL;
1308  break;
1309  case OP_PREV_ENTRY:
1310  case OP_MAIN_PREV_UNDELETED: /* hack */
1311  if (menu->current > 0)
1312  {
1313  menu->current--;
1314  op = OP_VIEW_ATTACH;
1315  }
1316  else
1317  op = OP_NULL;
1318  break;
1319  case OP_EDIT_TYPE:
1320  /* when we edit the content-type, we should redisplay the attachment
1321  * immediately */
1323  if (recv)
1324  recvattach_edit_content_type(actx, menu, e);
1325  else
1327 
1328  menu->redraw |= REDRAW_INDEX;
1329  op = OP_VIEW_ATTACH;
1330  break;
1331  /* functions which are passed through from the pager */
1332  case OP_CHECK_TRADITIONAL:
1334  {
1335  op = OP_NULL;
1336  break;
1337  }
1338  /* fallthrough */
1339  case OP_ATTACH_COLLAPSE:
1340  if (recv)
1341  return op;
1342  /* fallthrough */
1343  default:
1344  op = OP_NULL;
1345  }
1346  } while (op != OP_NULL);
1347 
1348  return op;
1349 }
1350 
1361 void mutt_generate_recvattach_list(struct AttachCtx *actx, struct Email *e,
1362  struct Body *parts, FILE *fp,
1363  int parent_type, int level, bool decrypted)
1364 {
1365  struct Body *m = NULL;
1366  struct Body *new_body = NULL;
1367  FILE *fp_new = NULL;
1369  int need_secured, secured;
1370 
1371  for (m = parts; m; m = m->next)
1372  {
1373  need_secured = 0;
1374  secured = 0;
1375 
1376  if (((WithCrypto & APPLICATION_SMIME) != 0) && (type = mutt_is_application_smime(m)))
1377  {
1378  need_secured = 1;
1379 
1380  if (type & SEC_ENCRYPT)
1381  {
1382  if (!crypt_valid_passphrase(APPLICATION_SMIME))
1383  goto decrypt_failed;
1384 
1385  if (e->env)
1387  }
1388 
1389  secured = !crypt_smime_decrypt_mime(fp, &fp_new, m, &new_body);
1390  /* If the decrypt/verify-opaque doesn't generate mime output, an empty
1391  * text/plain type will still be returned by mutt_read_mime_header().
1392  * We can't distinguish an actual part from a failure, so only use a
1393  * text/plain that results from a single top-level part. */
1394  if (secured && (new_body->type == TYPE_TEXT) &&
1395  mutt_istr_equal("plain", new_body->subtype) && ((parts != m) || m->next))
1396  {
1397  mutt_body_free(&new_body);
1398  mutt_file_fclose(&fp_new);
1399  goto decrypt_failed;
1400  }
1401 
1402  if (secured && (type & SEC_ENCRYPT))
1403  e->security |= SMIME_ENCRYPT;
1404  }
1405 
1406  if (((WithCrypto & APPLICATION_PGP) != 0) &&
1408  {
1409  need_secured = 1;
1410 
1411  if (!crypt_valid_passphrase(APPLICATION_PGP))
1412  goto decrypt_failed;
1413 
1414  secured = !crypt_pgp_decrypt_mime(fp, &fp_new, m, &new_body);
1415 
1416  if (secured)
1417  e->security |= PGP_ENCRYPT;
1418  }
1419 
1420  if (need_secured && secured)
1421  {
1422  mutt_actx_add_fp(actx, fp_new);
1423  mutt_actx_add_body(actx, new_body);
1424  mutt_generate_recvattach_list(actx, e, new_body, fp_new, parent_type, level, 1);
1425  continue;
1426  }
1427 
1428  decrypt_failed:
1429  /* Fall through and show the original parts if decryption fails */
1430  if (need_secured && !secured)
1431  mutt_error(_("Can't decrypt encrypted message"));
1432 
1433  /* Strip out the top level multipart */
1434  if ((m->type == TYPE_MULTIPART) && m->parts && !need_secured &&
1435  ((parent_type == -1) && !mutt_istr_equal("alternative", m->subtype)))
1436  {
1437  mutt_generate_recvattach_list(actx, e, m->parts, fp, m->type, level, decrypted);
1438  }
1439  else
1440  {
1441  struct AttachPtr *ap = mutt_mem_calloc(1, sizeof(struct AttachPtr));
1442  mutt_actx_add_attach(actx, ap);
1443 
1444  ap->body = m;
1445  ap->fp = fp;
1446  m->aptr = ap;
1447  ap->parent_type = parent_type;
1448  ap->level = level;
1449  ap->decrypted = decrypted;
1450 
1451  if (m->type == TYPE_MULTIPART)
1452  mutt_generate_recvattach_list(actx, e, m->parts, fp, m->type, level + 1, decrypted);
1453  else if (mutt_is_message_type(m->type, m->subtype))
1454  {
1455  mutt_generate_recvattach_list(actx, m->email, m->parts, fp, m->type,
1456  level + 1, decrypted);
1457  e->security |= m->email->security;
1458  }
1459  }
1460  }
1461 }
1462 
1467 void mutt_attach_init(struct AttachCtx *actx)
1468 {
1469  /* Collapse the attachments if '$digest_collapse' is set AND if...
1470  * the outer container is of type 'multipart/digest' */
1471  bool digest = mutt_istr_equal(actx->email->body->subtype, "digest");
1472 
1473  for (int i = 0; i < actx->idxlen; i++)
1474  {
1475  actx->idx[i]->body->tagged = false;
1476 
1477  /* OR an inner container is of type 'multipart/digest' */
1478  actx->idx[i]->body->collapsed =
1479  (C_DigestCollapse &&
1480  (digest || ((actx->idx[i]->body->type == TYPE_MULTIPART) &&
1481  mutt_istr_equal(actx->idx[i]->body->subtype, "digest"))));
1482  }
1483 }
1484 
1491 static void mutt_update_recvattach_menu(struct AttachCtx *actx, struct Menu *menu, bool init)
1492 {
1493  if (init)
1494  {
1495  mutt_generate_recvattach_list(actx, actx->email, actx->email->body,
1496  actx->fp_root, -1, 0, 0);
1497  mutt_attach_init(actx);
1498  menu->mdata = actx;
1499  }
1500 
1501  mutt_update_tree(actx);
1502 
1503  menu->max = actx->vcount;
1504 
1505  if (menu->current >= menu->max)
1506  menu->current = menu->max - 1;
1507  menu_check_recenter(menu);
1508  menu->redraw |= REDRAW_INDEX;
1509 }
1510 
1516 static void attach_collapse(struct AttachCtx *actx, struct Menu *menu)
1517 {
1518  int rindex, curlevel;
1519 
1520  CUR_ATTACH->body->collapsed = !CUR_ATTACH->body->collapsed;
1521  /* When expanding, expand all the children too */
1522  if (CUR_ATTACH->body->collapsed)
1523  return;
1524 
1525  curlevel = CUR_ATTACH->level;
1526  rindex = actx->v2r[menu->current] + 1;
1527 
1528  while ((rindex < actx->idxlen) && (actx->idx[rindex]->level > curlevel))
1529  {
1530  if (C_DigestCollapse && (actx->idx[rindex]->body->type == TYPE_MULTIPART) &&
1531  mutt_istr_equal(actx->idx[rindex]->body->subtype, "digest"))
1532  {
1533  actx->idx[rindex]->body->collapsed = true;
1534  }
1535  else
1536  {
1537  actx->idx[rindex]->body->collapsed = false;
1538  }
1539  rindex++;
1540  }
1541 }
1542 
1548 {
1549  int op = OP_NULL;
1550 
1551  struct Mailbox *m = Context ? Context->mailbox : NULL;
1552 
1553  /* make sure we have parsed this message */
1555 
1557 
1558  struct Message *msg = mx_msg_open(m, e->msgno);
1559  if (!msg)
1560  return;
1561 
1562  struct Menu *menu = mutt_menu_new(MENU_ATTACH);
1564  dlg->help_data = AttachHelp;
1565  dlg->help_menu = MENU_ATTACH;
1566 
1567  menu->title = _("Attachments");
1568  menu->make_entry = attach_make_entry;
1569  menu->tag = attach_tag;
1570  mutt_menu_push_current(menu);
1571 
1572  struct AttachCtx *actx = mutt_actx_new();
1573  actx->email = e;
1574  actx->fp_root = msg->fp;
1575  mutt_update_recvattach_menu(actx, menu, true);
1576 
1577  while (true)
1578  {
1579  if (op == OP_NULL)
1580  op = mutt_menu_loop(menu);
1581  window_redraw(dlg, true);
1582  if (!Context)
1583  return;
1584  switch (op)
1585  {
1586  case OP_ATTACH_VIEW_MAILCAP:
1588  e, actx, menu->win_index);
1589  menu->redraw = REDRAW_FULL;
1590  break;
1591 
1592  case OP_ATTACH_VIEW_TEXT:
1594  e, actx, menu->win_index);
1595  menu->redraw = REDRAW_FULL;
1596  break;
1597 
1598  case OP_ATTACH_VIEW_PAGER:
1600  actx, menu->win_index);
1601  menu->redraw = REDRAW_FULL;
1602  break;
1603 
1604  case OP_DISPLAY_HEADERS:
1605  case OP_VIEW_ATTACH:
1606  op = mutt_attach_display_loop(menu, op, e, actx, true);
1607  menu->redraw = REDRAW_FULL;
1608  continue;
1609 
1610  case OP_ATTACH_COLLAPSE:
1611  if (!CUR_ATTACH->body->parts)
1612  {
1613  mutt_error(_("There are no subparts to show"));
1614  break;
1615  }
1616  attach_collapse(actx, menu);
1617  mutt_update_recvattach_menu(actx, menu, false);
1618  break;
1619 
1620  case OP_FORGET_PASSPHRASE:
1622  break;
1623 
1624  case OP_EXTRACT_KEYS:
1626  {
1627  recvattach_extract_pgp_keys(actx, menu);
1628  menu->redraw = REDRAW_FULL;
1629  }
1630  break;
1631 
1632  case OP_CHECK_TRADITIONAL:
1633  if (((WithCrypto & APPLICATION_PGP) != 0) &&
1635  {
1636  e->security = crypt_query(NULL);
1637  menu->redraw = REDRAW_FULL;
1638  }
1639  break;
1640 
1641  case OP_PRINT:
1643  CUR_ATTACH->body);
1644  break;
1645 
1646  case OP_PIPE:
1648  CUR_ATTACH->body, false);
1649  break;
1650 
1651  case OP_SAVE:
1653  CUR_ATTACH->body, e, menu);
1654 
1655  if (!menu->tagprefix && C_Resolve && (menu->current < menu->max - 1))
1656  menu->current++;
1657 
1659  break;
1660 
1661  case OP_DELETE:
1663 
1664 #ifdef USE_POP
1665  if (m->type == MUTT_POP)
1666  {
1667  mutt_flushinp();
1668  mutt_error(_("Can't delete attachment from POP server"));
1669  break;
1670  }
1671 #endif
1672 
1673 #ifdef USE_NNTP
1674  if (m->type == MUTT_NNTP)
1675  {
1676  mutt_flushinp();
1677  mutt_error(_("Can't delete attachment from news server"));
1678  break;
1679  }
1680 #endif
1681 
1682  if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT))
1683  {
1684  mutt_message(_("Deletion of attachments from encrypted messages is "
1685  "unsupported"));
1686  break;
1687  }
1688  if ((WithCrypto != 0) && (e->security & (SEC_SIGN | SEC_PARTSIGN)))
1689  {
1690  mutt_message(_("Deletion of attachments from signed messages may "
1691  "invalidate the signature"));
1692  }
1693  if (!menu->tagprefix)
1694  {
1695  if (CUR_ATTACH->parent_type == TYPE_MULTIPART)
1696  {
1697  CUR_ATTACH->body->deleted = true;
1698  if (C_Resolve && (menu->current < menu->max - 1))
1699  {
1700  menu->current++;
1701  menu->redraw = REDRAW_MOTION_RESYNC;
1702  }
1703  else
1704  menu->redraw = REDRAW_CURRENT;
1705  }
1706  else
1707  {
1708  mutt_message(
1709  _("Only deletion of multipart attachments is supported"));
1710  }
1711  }
1712  else
1713  {
1714  for (int i = 0; i < menu->max; i++)
1715  {
1716  if (actx->idx[i]->body->tagged)
1717  {
1718  if (actx->idx[i]->parent_type == TYPE_MULTIPART)
1719  {
1720  actx->idx[i]->body->deleted = true;
1721  menu->redraw = REDRAW_INDEX;
1722  }
1723  else
1724  {
1725  mutt_message(
1726  _("Only deletion of multipart attachments is supported"));
1727  }
1728  }
1729  }
1730  }
1731  break;
1732 
1733  case OP_UNDELETE:
1735  if (!menu->tagprefix)
1736  {
1737  CUR_ATTACH->body->deleted = false;
1738  if (C_Resolve && (menu->current < menu->max - 1))
1739  {
1740  menu->current++;
1741  menu->redraw = REDRAW_MOTION_RESYNC;
1742  }
1743  else
1744  menu->redraw = REDRAW_CURRENT;
1745  }
1746  else
1747  {
1748  for (int i = 0; i < menu->max; i++)
1749  {
1750  if (actx->idx[i]->body->tagged)
1751  {
1752  actx->idx[i]->body->deleted = false;
1753  menu->redraw = REDRAW_INDEX;
1754  }
1755  }
1756  }
1757  break;
1758 
1759  case OP_RESEND:
1760  CHECK_ATTACH;
1761  mutt_attach_resend(CUR_ATTACH->fp, actx,
1762  menu->tagprefix ? NULL : CUR_ATTACH->body);
1763  menu->redraw = REDRAW_FULL;
1764  break;
1765 
1766  case OP_BOUNCE_MESSAGE:
1767  CHECK_ATTACH;
1768  mutt_attach_bounce(m, CUR_ATTACH->fp, actx,
1769  menu->tagprefix ? NULL : CUR_ATTACH->body);
1770  menu->redraw = REDRAW_FULL;
1771  break;
1772 
1773  case OP_FORWARD_MESSAGE:
1774  CHECK_ATTACH;
1775  mutt_attach_forward(CUR_ATTACH->fp, e, actx,
1776  menu->tagprefix ? NULL : CUR_ATTACH->body, SEND_NO_FLAGS);
1777  menu->redraw = REDRAW_FULL;
1778  break;
1779 
1780 #ifdef USE_NNTP
1781  case OP_FORWARD_TO_GROUP:
1782  CHECK_ATTACH;
1783  mutt_attach_forward(CUR_ATTACH->fp, e, actx,
1784  menu->tagprefix ? NULL : CUR_ATTACH->body, SEND_NEWS);
1785  menu->redraw = REDRAW_FULL;
1786  break;
1787 
1788  case OP_FOLLOWUP:
1789  CHECK_ATTACH;
1790 
1791  if (!CUR_ATTACH->body->email->env->followup_to ||
1792  !mutt_istr_equal(CUR_ATTACH->body->email->env->followup_to,
1793  "poster") ||
1795  _("Reply by mail as poster prefers?")) != MUTT_YES))
1796  {
1797  mutt_attach_reply(CUR_ATTACH->fp, e, actx,
1798  menu->tagprefix ? NULL : CUR_ATTACH->body,
1799  SEND_NEWS | SEND_REPLY);
1800  menu->redraw = REDRAW_FULL;
1801  break;
1802  }
1803 #endif
1804  /* fallthrough */
1805  case OP_REPLY:
1806  case OP_GROUP_REPLY:
1807  case OP_GROUP_CHAT_REPLY:
1808  case OP_LIST_REPLY:
1809  {
1810  CHECK_ATTACH;
1811 
1812  SendFlags flags = SEND_REPLY;
1813  if (op == OP_GROUP_REPLY)
1814  flags |= SEND_GROUP_REPLY;
1815  else if (op == OP_GROUP_CHAT_REPLY)
1816  flags |= SEND_GROUP_CHAT_REPLY;
1817  else if (op == OP_LIST_REPLY)
1818  flags |= SEND_LIST_REPLY;
1819 
1820  mutt_attach_reply(CUR_ATTACH->fp, e, actx,
1821  menu->tagprefix ? NULL : CUR_ATTACH->body, flags);
1822  menu->redraw = REDRAW_FULL;
1823  break;
1824  }
1825 
1826  case OP_COMPOSE_TO_SENDER:
1827  CHECK_ATTACH;
1828  mutt_attach_mail_sender(CUR_ATTACH->fp, e, actx,
1829  menu->tagprefix ? NULL : CUR_ATTACH->body);
1830  menu->redraw = REDRAW_FULL;
1831  break;
1832 
1833  case OP_EDIT_TYPE:
1834  recvattach_edit_content_type(actx, menu, e);
1835  menu->redraw |= REDRAW_INDEX;
1836  break;
1837 
1838  case OP_EXIT:
1839  mx_msg_close(m, &msg);
1840 
1841  e->attach_del = false;
1842  for (int i = 0; i < actx->idxlen; i++)
1843  {
1844  if (actx->idx[i]->body && actx->idx[i]->body->deleted)
1845  {
1846  e->attach_del = true;
1847  break;
1848  }
1849  }
1850  if (e->attach_del)
1851  e->changed = true;
1852 
1853  mutt_actx_free(&actx);
1854 
1855  mutt_menu_pop_current(menu);
1856  mutt_menu_free(&menu);
1858  return;
1859  }
1860 
1861  op = OP_NULL;
1862  }
1863 
1864  /* not reached */
1865 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
Convenience wrapper for the gui headers.
The "current" mailbox.
Definition: context.h:38
bool C_AttachSplit
Config: Save/print/pipe tagged messages individually.
Definition: recvattach.c:75
void mutt_decode_attachment(struct Body *b, struct State *s)
Decode an email&#39;s attachment.
Definition: handler.c:1845
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:77
#define REDRAW_FULL
Redraw everything.
Definition: mutt_menu.h:45
void mutt_actx_free(struct AttachCtx **ptr)
Free an Attachment Context.
Definition: attach.c:140
char * filename
when sending a message, this is the file to which this structure refers
Definition: body.h:46
Manage keymappings.
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
#define NONULL(x)
Definition: string2.h:37
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
An email to which things will be attached.
Definition: attach.h:34
Miscellaneous email parsing routines.
char * C_MessageFormat
Config: printf-like format string for listing attached messages.
Definition: recvattach.c:77
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:206
#define WithCrypto
Definition: lib.h:123
void mutt_make_string_flags(char *buf, size_t buflen, int cols, const char *s, struct Mailbox *m, int inpgr, struct Email *e, MuttFormatFlags flags)
Create formatted strings using mailbox expandos.
Definition: hdrline.c:1415
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1366
static bool has_a_message(struct Body *body)
Determine if the Body has a message (to save)
Definition: recvattach.c:497
The envelope/body of an email.
Definition: email.h:37
#define state_puts(STATE, STR)
Definition: state.h:55
#define MUTT_CLEAR
Clear input if printable character is pressed.
Definition: mutt.h:62
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
void mutt_actx_add_fp(struct AttachCtx *actx, FILE *fp_new)
Save a File handle to the Attachment Context.
Definition: attach.c:62
static const struct Mapping AttachHelp[]
Help Bar for the Attachment selection dialog.
Definition: recvattach.c:94
#define mutt_perror(...)
Definition: logging.h:85
bool mutt_rfc3676_is_format_flowed(struct Body *b)
Is the Email "format-flowed"?
Definition: rfc3676.c:387
void mutt_update_encoding(struct Body *a, struct ConfigSubset *sub)
Update the encoding type.
Definition: sendlib.c:905
GUI selectable list of items.
Definition: mutt_menu.h:52
struct Body * body
List of MIME parts.
Definition: email.h:91
static void attach_make_entry(char *buf, size_t buflen, struct Menu *menu, int line)
Format a menu item for the attachment list - Implements Menu::make_entry()
Definition: recvattach.c:446
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:52
#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.
size_t mutt_buffer_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer&#39;s contents to another Buffer.
Definition: buffer.c:445
void mutt_generate_recvattach_list(struct AttachCtx *actx, struct Email *e, struct Body *parts, FILE *fp, int parent_type, int level, bool decrypted)
Create a list of attachments.
Definition: recvattach.c:1361
The "currently-open" mailbox.
static const char * Mailbox_is_read_only
Definition: recvattach.c:81
Convenience wrapper for the send headers.
const char * mutt_path_basename(const char *f)
Find the last component for a pathname.
Definition: path.c:329
#define MUTT_FORMAT_FORCESUBJ
Print the subject even if unchanged.
Definition: format_flags.h:31
#define mutt_message(...)
Definition: logging.h:83
int help_menu
Menu for key bindings, e.g. MENU_PAGER.
Definition: mutt_window.h:134
Attach Dialog, dlg_select_attachment()
Definition: mutt_window.h:75
struct Email * email
Used by recvattach for updating.
Definition: attach.h:51
int oldcurrent
For driver use only.
Definition: mutt_menu.h:76
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition: file.c:1347
char * C_AttachSaveWithoutPrompting
Config: If true, then don&#39;t prompt to save.
Definition: recvattach.c:73
#define SMIME_ENCRYPT
Definition: lib.h:109
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:85
No flags set.
Definition: mutt_attach.h:56
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:919
char * C_AttachSep
Config: Separator to add between saved/printed/piped attachments.
Definition: recvattach.c:74
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:49
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
#define MUTT_FORMAT_ARROWCURSOR
Reserve space for arrow_cursor.
Definition: format_flags.h:35
bool noconv
Don&#39;t do character set conversion.
Definition: body.h:73
static void prepend_savedir(struct Buffer *buf)
Add $attach_save_dir to the beginning of a path.
Definition: recvattach.c:472
void crypt_forget_passphrase(void)
Forget a passphrase and display a message.
Definition: crypt.c:92
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:195
RFC1524 Mailcap routines.
static void print_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top, struct State *state)
Print a list of Attachments.
Definition: recvattach.c:1096
#define PGP_ENCRYPT
Definition: lib.h:103
FILE * fp_root
Used by recvattach for updating.
Definition: attach.h:52
String manipulation buffer.
Definition: buffer.h:33
bool C_DigestCollapse
Config: Hide the subparts of a multipart/digest.
Definition: recvattach.c:76
void mutt_parse_mime_message(struct Mailbox *m, struct Email *e)
Parse a MIME email.
Definition: mutt_parse.c:49
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
#define _(a)
Definition: message.h:28
bool changed
Email has been edited.
Definition: email.h:48
struct Body * next
next attachment in the list
Definition: body.h:53
short idxlen
Number of attachmentes.
Definition: attach.h:55
FILE * fp_out
File to write to.
Definition: state.h:47
Force viewing as text.
Definition: mutt_attach.h:44
A division of the screen.
Definition: mutt_window.h:115
void mutt_actx_add_attach(struct AttachCtx *actx, struct AttachPtr *attach)
Add an Attachment to an Attachment Context.
Definition: attach.c:40
uint16_t SecurityFlags
Flags, e.g. SEC_ENCRYPT.
Definition: lib.h:83
#define MUTT_MESSAGE_HOOK
message-hook: run before displaying a message
Definition: hook.h:52
static int query_save_attachment(FILE *fp, struct Body *body, struct Email *e, char **directory)
Ask the user if we should save the attachment.
Definition: recvattach.c:576
#define MUTT_PRINTING
Are we printing? - MUTT_DISPLAY "light".
Definition: state.h:37
int mutt_save_attachment(FILE *fp, struct Body *m, const char *path, enum SaveAttach opt, struct Email *e)
Save an attachment.
Definition: mutt_attach.c:873
View using default method.
Definition: mutt_attach.h:42
uint16_t SendFlags
Flags for mutt_send_message(), e.g. SEND_REPLY.
Definition: send.h:37
#define MUTT_FORMAT_STAT_FILE
Used by attach_format_str.
Definition: format_flags.h:34
void mutt_default_save(char *path, size_t pathlen, struct Email *e)
Find the default save path for an email.
Definition: hook.c:654
Flags to control mutt_expando_format()
All user-callable functions.
Container for Accounts, Notifications.
Definition: neomutt.h:36
FILE * fp_in
File to read from.
Definition: state.h:46
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:599
struct MuttWindow * dialog_create_simple_index(struct Menu *menu, enum WindowType type)
Create a simple index Dialog.
Definition: dialog.c:165
int mutt_save_confirm(const char *s, struct stat *st)
Ask the user to save.
Definition: muttlib.c:1350
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
The body of an email.
Definition: body.h:34
Lower left corner.
Definition: mutt_thread.h:56
unsigned int disposition
content-disposition, ContentDisposition
Definition: body.h:67
Convenience wrapper for the config headers.
#define CUR_ATTACH
Definition: recvattach.c:91
bool mailcap_lookup(struct Body *a, char *type, size_t typelen, struct MailcapEntry *entry, enum MailcapLookup opt)
Find given type in the list of mailcap files.
Definition: mailcap.c:465
int attach_tag(struct Menu *menu, int sel, int act)
Tag an attachment - Implements Menu::tag()
Definition: recvattach.c:458
char * tree
Tree characters to display.
Definition: attach.h:39
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:40
Some miscellaneous functions.
void mutt_attach_reply(FILE *fp, struct Email *e, struct AttachCtx *actx, struct Body *e_cur, SendFlags flags)
Attach a reply.
Definition: recvcmd.c:919
StateFlags flags
Flags, e.g. MUTT_DISPLAY.
Definition: state.h:49
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1205
bool crypt_valid_passphrase(SecurityFlags flags)
Check that we have a usable passphrase, ask if not.
Definition: crypt.c:134
size_t dsize
Length of data.
Definition: buffer.h:37
bool attach_qualifies
This attachment should be counted.
Definition: body.h:83
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1447
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: curs_lib.c:379
struct Mailbox * mailbox
Definition: context.h:50
Parse and execute user-defined hooks.
SecurityFlags mutt_is_multipart_encrypted(struct Body *b)
Does the message have encrypted parts?
Definition: crypt.c:447
Many unsorted constants and some structs.
API for mailboxes.
Mailcap print field.
Definition: mailcap.h:61
const char * title
Title of this menu.
Definition: mutt_menu.h:54
void mutt_window_clearline(struct MuttWindow *win, int row)
Clear a row of a Window.
Definition: mutt_window.c:232
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
int parent_type
Type of parent attachment, e.g. TYPE_MULTIPART.
Definition: attach.h:38
struct Envelope * env
Envelope information.
Definition: email.h:90
Convenience wrapper for the core headers.
#define ENCODING(x)
Definition: mime.h:92
char * mutt_body_get_charset(struct Body *b, char *buf, size_t buflen)
Get a body&#39;s character set.
Definition: body.c:131
signed short attach_count
Number of attachments.
Definition: body.h:59
int bool_str_toggle(struct ConfigSubset *sub, const char *name, struct Buffer *err)
Toggle the value of a bool.
Definition: bool.c:213
struct AttachCtx * mutt_actx_new(void)
Create a new Attachment Context.
Definition: attach.c:131
#define SEND_LIST_REPLY
Reply to mailing list.
Definition: send.h:43
bool tagged
This attachment is tagged.
Definition: body.h:70
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:66
WHERE char * C_PrintCommand
Config: External command to print a message.
Definition: mutt_globals.h:103
Base-64 encoded text.
Definition: mime.h:52
void crypt_pgp_extract_key_from_attachment(FILE *fp, struct Body *top)
Wrapper for CryptModuleSpecs::pgp_extract_key_from_attachment()
Definition: cryptglue.c:393
#define REDRAW_MOTION
Redraw after moving the menu list.
Definition: mutt_menu.h:41
Usenet network mailbox type; talk to an NNTP server.
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
void mutt_buffer_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:181
bool collapsed
Used by recvattach.
Definition: body.h:82
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:883
char * subtype
content-type subtype
Definition: body.h:37
#define REDRAW_MOTION_RESYNC
Redraw any changing the menu selection.
Definition: mutt_menu.h:42
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:57
#define MUTT_CMD
Do completion on previous word.
Definition: mutt.h:60
static void mutt_update_v2r(struct AttachCtx *actx)
Update the virtual list of attachments.
Definition: recvattach.c:122
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:98
bool decrypted
Not part of message as stored in the email->body.
Definition: attach.h:43
void mutt_attach_bounce(struct Mailbox *m, FILE *fp, struct AttachCtx *actx, struct Body *cur)
Bounce function, from the attachment menu.
Definition: recvcmd.c:168
#define mutt_mktemp(buf, buflen)
Definition: muttlib.h:74
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:120
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:97
static int save_without_prompting(FILE *fp, struct Body *body, struct Email *e)
Save the attachment, without prompting each time.
Definition: recvattach.c:673
void * mdata
Extra data for the current menu.
Definition: mutt_menu.h:55
&#39;POP3&#39; Mailbox type
Definition: mailbox.h:55
struct MuttWindow * MessageWindow
Message Window, ":set", etc.
Definition: mutt_window.c:47
A local copy of an email.
Definition: mx.h:82
void mutt_endwin(void)
Shutdown curses/slang.
Definition: curs_lib.c:571
Select an attachment.
Definition: keymap.h:75
Create/manipulate threading in emails.
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
A mailbox.
Definition: mailbox.h:81
static void recvattach_edit_content_type(struct AttachCtx *actx, struct Menu *menu, struct Email *e)
Edit the content type of an attachment.
Definition: recvattach.c:1253
#define PATH_MAX
Definition: mutt.h:44
int mutt_attach_display_loop(struct Menu *menu, int op, struct Email *e, struct AttachCtx *actx, bool recv)
Event loop for the Attachment menu.
Definition: recvattach.c:1283
Right arrow.
Definition: mutt_thread.h:62
int num
Attachment index number.
Definition: attach.h:41
void mutt_attach_init(struct AttachCtx *actx)
Create a new Attachment context.
Definition: recvattach.c:1467
bool tagprefix
Definition: mutt_menu.h:61
static void recvattach_extract_pgp_keys(struct AttachCtx *actx, struct Menu *menu)
Extract PGP keys from attachments.
Definition: recvattach.c:1207
int mutt_decode_save_attachment(FILE *fp, struct Body *m, const char *path, int displaying, enum SaveAttach opt)
Decode, then save an attachment.
Definition: mutt_attach.c:1004
Manage where the email is piped to external commands.
struct AttachPtr * aptr
Menu information, used in recvattach.c.
Definition: body.h:57
Type: &#39;text/*&#39;.
Definition: mime.h:38
char * dptr
Current read/write position.
Definition: buffer.h:36
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1662
void crypt_smime_getkeys(struct Envelope *env)
Wrapper for CryptModuleSpecs::smime_getkeys()
Definition: cryptglue.c:455
bool mutt_can_decode(struct Body *a)
Will decoding the attachment produce any output.
Definition: handler.c:1805
const struct Mapping * help_data
Data for the Help Bar.
Definition: mutt_window.h:135
char * data
Pointer to data.
Definition: buffer.h:35
void dialog_destroy_simple_index(struct MuttWindow **ptr)
Destroy a simple index Dialog.
Definition: dialog.c:209
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:54
static void query_pipe_attachment(const char *command, FILE *fp, struct Body *body, bool filter)
Ask the user if we should pipe the attachment.
Definition: recvattach.c:840
static void mutt_update_recvattach_menu(struct AttachCtx *actx, struct Menu *menu, bool init)
Update the Attachment Menu.
Definition: recvattach.c:1491
char * C_AttachSaveDir
Config: Default directory where attachments are saved.
Definition: recvattach.c:72
RFC3676 Format Flowed routines.
static void pipe_attachment_list(const char *command, struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top, bool filter, struct State *state)
Pipe a list of attachments to a command.
Definition: recvattach.c:978
FILE * fp
Used in the recvattach menu.
Definition: attach.h:37
GUI present the user with a selectable list.
API for encryption/signing of emails.
SecurityFlags mutt_is_malformed_multipart_pgp_encrypted(struct Body *b)
Check for malformed layout.
Definition: crypt.c:508
int crypt_smime_decrypt_mime(FILE *fp_in, FILE **fp_out, struct Body *b, struct Body **cur)
Wrapper for CryptModuleSpecs::decrypt_mime()
Definition: cryptglue.c:431
void mutt_rfc3676_space_unstuff_attachment(struct Body *b, const char *filename)
Unstuff attachments.
Definition: rfc3676.c:511
Left T-piece.
Definition: mutt_thread.h:58
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
#define SEND_NEWS
Reply to a news article.
Definition: send.h:54
SecurityFlags mutt_is_application_smime(struct Body *m)
Does the message use S/MIME?
Definition: crypt.c:610
static void pipe_attachment(FILE *fp, struct Body *b, struct State *state)
Pipe the attachment to a command.
Definition: recvattach.c:882
Handling of email attachments.
void dlg_select_attachment(struct Email *e)
Show the attachments in a Menu.
Definition: recvattach.c:1547
int(* tag)(struct Menu *menu, int sel, int act)
Tag some menu items.
Definition: mutt_menu.h:107
char * description
content-description
Definition: body.h:40
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:433
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib...
Definition: email.h:39
static int recvattach_pgp_check_traditional(struct AttachCtx *actx, struct Menu *menu)
Is the Attachment inline PGP?
Definition: recvattach.c:1231
static bool can_print(struct AttachCtx *actx, struct Body *top, bool tag)
Do we know how to print this attachment type?
Definition: recvattach.c:1056
View attachment in pager using copiousoutput mailcap.
Definition: mutt_attach.h:45
Force viewing using mailcap entry.
Definition: mutt_attach.h:43
#define SEND_REPLY
Reply to sender.
Definition: send.h:41
#define CHECK_READONLY
Definition: recvattach.c:83
#define MUTT_FILE
Do file completion.
Definition: mutt.h:58
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
unsigned int type
content-type primary type, ContentType
Definition: body.h:65
int max
Number of entries in the menu.
Definition: mutt_menu.h:57
int mutt_check_overwrite(const char *attname, const char *path, struct Buffer *fname, enum SaveAttach *opt, char **directory)
Ask the user if overwriting is necessary.
Definition: muttlib.c:621
#define mutt_buffer_get_field(field, buf, complete)
Definition: curs_lib.h:85
void mutt_print_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top)
Print a list of Attachments.
Definition: recvattach.c:1164
MuttRedrawFlags redraw
When to redraw the screen.
Definition: mutt_menu.h:58
void mutt_attach_forward(FILE *fp, struct Email *e, struct AttachCtx *actx, struct Body *cur, SendFlags flags)
Forward an Attachment.
Definition: recvcmd.c:768
#define SEC_SIGN
Email is signed.
Definition: lib.h:86
int crypt_pgp_check_traditional(FILE *fp, struct Body *b, bool just_one)
Wrapper for CryptModuleSpecs::pgp_check_traditional()
Definition: cryptglue.c:281
int mutt_any_key_to_continue(const char *s)
Prompt the user to &#39;press any key&#39; and wait.
Definition: curs_lib.c:604
Keep track when processing files.
bool deleted
Attachment marked for deletion.
Definition: body.h:71
static void attach_collapse(struct AttachCtx *actx, struct Menu *menu)
Close the tree of the current attachment.
Definition: recvattach.c:1516
#define TYPE(body)
Definition: mime.h:89
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: curs_lib.c:517
void mutt_actx_entries_free(struct AttachCtx *actx)
Free entries in an Attachment Context.
Definition: attach.c:103
Horizontal line.
Definition: mutt_thread.h:59
#define SEND_GROUP_REPLY
Reply to all.
Definition: send.h:42
int crypt_pgp_decrypt_mime(FILE *fp_in, FILE **fp_out, struct Body *b, struct Body **cur)
Wrapper for CryptModuleSpecs::decrypt_mime()
Definition: cryptglue.c:207
Type: &#39;multipart/*&#39;.
Definition: mime.h:37
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:323
void mutt_format_s(char *buf, size_t buflen, const char *prec, const char *s)
Format a simple string.
Definition: curs_lib.c:1244
Routines for managing attachments.
Log at debug level 1.
Definition: logging.h:40
Send/reply with an attachment.
static int save_attachment_flowed_helper(FILE *fp, struct Body *b, const char *path, enum SaveAttach flags, struct Email *e)
Helper for unstuffing attachments.
Definition: recvattach.c:529
#define REDRAW_INDEX
Redraw the index.
Definition: mutt_menu.h:40
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:271
short * v2r
Mapping from virtual to real attachment.
Definition: attach.h:58
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t callback, intptr_t data, MuttFormatFlags flags)
Expand expandos (x) in a string.
Definition: muttlib.c:773
#define mutt_error(...)
Definition: logging.h:84
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition: body.h:69
Quoted-printable text.
Definition: mime.h:51
void mutt_update_tree(struct AttachCtx *actx)
Refresh the list of attachments.
Definition: recvattach.c:151
FILE * fp
pointer to the message data
Definition: mx.h:84
Append to existing file.
Definition: mutt_attach.h:57
#define FREE(x)
Definition: memory.h:40
void mutt_save_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top, struct Email *e, struct Menu *menu)
Save a list of attachments.
Definition: recvattach.c:723
const char * attach_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, intptr_t data, MuttFormatFlags flags)
Format a string for the attachment menu - Implements format_t.
Definition: recvattach.c:217
size_t mutt_buffer_concat_path(struct Buffer *buf, const char *dir, const char *fname)
Join a directory name and a filename.
Definition: buffer.c:374
Mapping between user-readable string and a constant.
Definition: mapping.h:31
WHERE char * C_AttachFormat
Config: printf-like format string for the attachment menu.
Definition: mutt_globals.h:87
Keep track when processing files.
Definition: state.h:44
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:1089
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:39
#define MUTT_FORMAT_OPTIONAL
Allow optional field processing.
Definition: format_flags.h:33
void window_redraw(struct MuttWindow *win, bool force)
Reflow, recalc and repaint a tree of Windows.
Definition: mutt_window.c:747
char * d_filename
filename to be used for the content-disposition header.
Definition: body.h:47
#define PGP_TRADITIONAL_CHECKED
Email has a traditional (inline) signature.
Definition: lib.h:99
Hundreds of global variables to back the user variables.
Handling of global boolean variables.
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
void mutt_message_hook(struct Mailbox *m, struct Email *e, HookFlags type)
Perform a message hook.
Definition: hook.c:573
WHERE unsigned char C_Print
Config: Confirm before printing a message.
Definition: mutt_globals.h:128
#define SEND_GROUP_CHAT_REPLY
Reply to all recipients preserving To/Cc.
Definition: send.h:53
static const char * Function_not_permitted
Definition: recvattach.c:105
bool mutt_edit_content_type(struct Email *e, struct Body *b, FILE *fp)
Edit the content type of an attachment.
Definition: commands.c:1279
int current
Current entry.
Definition: mutt_menu.h:56
struct MuttWindow * win_index
Definition: mutt_menu.h:63
Convenience wrapper for the library headers.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
#define CHECK_ATTACH
Definition: recvattach.c:108
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
Decide how to display email content.
#define N_(a)
Definition: message.h:32
struct Email * email
header information for message/rfc822
Definition: body.h:55
A set of attachments.
Definition: attach.h:49
int level
Nesting depth of attachment.
Definition: attach.h:40
struct AttachPtr ** idx
Array of attachments.
Definition: attach.h:54
SecurityFlags crypt_query(struct Body *m)
Check out the type of encryption used.
Definition: crypt.c:685
void(* make_entry)(char *buf, size_t buflen, struct Menu *menu, int line)
Format a item for a menu.
Definition: mutt_menu.h:88
void mutt_attach_resend(FILE *fp, struct AttachCtx *actx, struct Body *cur)
resend-message, from the attachment menu
Definition: recvcmd.c:295
int mutt_pipe_attachment(FILE *fp, struct Body *b, const char *path, char *outfile)
Pipe an attachment to a command.
Definition: mutt_attach.c:707
struct Message * mx_msg_open(struct Mailbox *m, int msgno)
return a stream pointer for a message
Definition: mx.c:1159
#define SEC_PARTSIGN
Not all parts of the email is signed.
Definition: lib.h:89
#define REDRAW_CURRENT
Redraw the current line of the menu.
Definition: mutt_menu.h:43
void mutt_pipe_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top, bool filter)
Pipe a list of attachments to a command.
Definition: recvattach.c:1009
unsigned char C_FollowupToPoster
Config: (nntp) Reply to the poster if &#39;poster&#39; is in the &#39;Followup-To&#39; header.
Definition: config.c:38
WHERE bool C_Resolve
Config: Move to the next email whenever a command modifies an email.
Definition: mutt_globals.h:155
void mutt_actx_add_body(struct AttachCtx *actx, struct Body *new_body)
Add an email box to an Attachment Context.
Definition: attach.c:83
void mutt_format_s_tree(char *buf, size_t buflen, const char *prec, const char *s)
Format a simple string with tree characters.
Definition: curs_lib.c:1256
short vcount
The number of virtual attachments.
Definition: attach.h:59
int msgno
Number displayed to the user.
Definition: email.h:87
SaveAttach
Options for saving attachments.
Definition: mutt_attach.h:54
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:40
int mutt_view_attachment(FILE *fp, struct Body *a, enum ViewAttachMode mode, struct Email *e, struct AttachCtx *actx, struct MuttWindow *win)
View an attachment.
Definition: mutt_attach.c:414
WHERE bool C_WaitKey
Config: Prompt to press a key after running external commands.
Definition: mutt_globals.h:169
int mutt_print_attachment(FILE *fp, struct Body *a)
Print out an attachment.
Definition: mutt_attach.c:1103
struct Body * body
Attachment.
Definition: attach.h:36