NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
recvattach.c
Go to the documentation of this file.
1 
71 #include "config.h"
72 #include <limits.h>
73 #include <stdbool.h>
74 #include <stdio.h>
75 #include <string.h>
76 #include <sys/stat.h>
77 #include <unistd.h>
78 #include "mutt/lib.h"
79 #include "config/lib.h"
80 #include "email/lib.h"
81 #include "core/lib.h"
82 #include "gui/lib.h"
83 #include "mutt.h"
84 #include "recvattach.h"
85 #include "menu/lib.h"
86 #include "ncrypt/lib.h"
87 #include "question/lib.h"
88 #include "send/lib.h"
89 #include "attachments.h"
90 #include "commands.h"
91 #include "format_flags.h"
92 #include "handler.h"
93 #include "hdrline.h"
94 #include "hook.h"
95 #include "mailcap.h"
96 #include "mutt_attach.h"
97 #include "mutt_thread.h"
98 #include "muttlib.h"
99 #include "opcodes.h"
100 #include "options.h"
101 #include "recvcmd.h"
102 #include "rfc3676.h"
103 #ifdef ENABLE_NLS
104 #include <libintl.h>
105 #endif
106 
107 static void mutt_update_recvattach_menu(struct ConfigSubset *sub, struct AttachCtx *actx,
108  struct Menu *menu, bool init);
109 
111 static const struct Mapping AttachHelp[] = {
112  // clang-format off
113  { N_("Exit"), OP_EXIT },
114  { N_("Save"), OP_SAVE },
115  { N_("Pipe"), OP_PIPE },
116  { N_("Print"), OP_PRINT },
117  { N_("Help"), OP_HELP },
118  { NULL, 0 },
119  // clang-format on
120 };
121 
128 static struct AttachPtr *current_attachment(struct AttachCtx *actx, struct Menu *menu)
129 {
130  const int virt = menu_get_index(menu);
131  const int index = actx->v2r[virt];
132 
133  return actx->idx[index];
134 }
135 
140 static bool check_attach(void)
141 {
142  if (OptAttachMsg)
143  {
144  mutt_flushinp();
145  mutt_error(_("Function not permitted in attach-message mode"));
146  return true;
147  }
148 
149  return false;
150 }
151 
157 static bool check_readonly(struct Mailbox *m)
158 {
159  if (!m || m->readonly)
160  {
161  mutt_flushinp();
162  mutt_error(_("Mailbox is read-only"));
163  return true;
164  }
165 
166  return false;
167 }
168 
175 static void mutt_update_v2r(struct AttachCtx *actx)
176 {
177  int vindex, rindex, curlevel;
178 
179  vindex = 0;
180  rindex = 0;
181 
182  while (rindex < actx->idxlen)
183  {
184  actx->v2r[vindex++] = rindex;
185  if (actx->idx[rindex]->body->collapsed)
186  {
187  curlevel = actx->idx[rindex]->level;
188  do
189  {
190  rindex++;
191  } while ((rindex < actx->idxlen) && (actx->idx[rindex]->level > curlevel));
192  }
193  else
194  rindex++;
195  }
196 
197  actx->vcount = vindex;
198 }
199 
204 void mutt_update_tree(struct AttachCtx *actx)
205 {
206  char buf[256];
207  char *s = NULL;
208 
209  mutt_update_v2r(actx);
210 
211  for (int vindex = 0; vindex < actx->vcount; vindex++)
212  {
213  const int rindex = actx->v2r[vindex];
214  actx->idx[rindex]->num = vindex;
215  if ((2 * (actx->idx[rindex]->level + 2)) < sizeof(buf))
216  {
217  if (actx->idx[rindex]->level)
218  {
219  s = buf + 2 * (actx->idx[rindex]->level - 1);
220  *s++ = (actx->idx[rindex]->body->next) ? MUTT_TREE_LTEE : MUTT_TREE_LLCORNER;
221  *s++ = MUTT_TREE_HLINE;
222  *s++ = MUTT_TREE_RARROW;
223  }
224  else
225  s = buf;
226  *s = '\0';
227  }
228 
229  if (actx->idx[rindex]->tree)
230  {
231  if (!mutt_str_equal(actx->idx[rindex]->tree, buf))
232  mutt_str_replace(&actx->idx[rindex]->tree, buf);
233  }
234  else
235  actx->idx[rindex]->tree = mutt_str_dup(buf);
236 
237  if (((2 * (actx->idx[rindex]->level + 2)) < sizeof(buf)) &&
238  actx->idx[rindex]->level)
239  {
240  s = buf + 2 * (actx->idx[rindex]->level - 1);
241  *s++ = (actx->idx[rindex]->body->next) ? '\005' : '\006';
242  *s++ = '\006';
243  }
244  }
245 }
246 
270 const char *attach_format_str(char *buf, size_t buflen, size_t col, int cols, char op,
271  const char *src, const char *prec, const char *if_str,
272  const char *else_str, intptr_t data, MuttFormatFlags flags)
273 {
274  char fmt[128];
275  char charset[128];
276  struct AttachPtr *aptr = (struct AttachPtr *) data;
277  bool optional = (flags & MUTT_FORMAT_OPTIONAL);
278 
279  switch (op)
280  {
281  case 'C':
282  if (!optional)
283  {
284  if (mutt_is_text_part(aptr->body) &&
285  mutt_body_get_charset(aptr->body, charset, sizeof(charset)))
286  {
287  mutt_format_s(buf, buflen, prec, charset);
288  }
289  else
290  mutt_format_s(buf, buflen, prec, "");
291  }
292  else if (!mutt_is_text_part(aptr->body) ||
293  !mutt_body_get_charset(aptr->body, charset, sizeof(charset)))
294  {
295  optional = false;
296  }
297  break;
298  case 'c':
299  /* XXX */
300  if (!optional)
301  {
302  snprintf(fmt, sizeof(fmt), "%%%sc", prec);
303  snprintf(buf, buflen, fmt,
304  ((aptr->body->type != TYPE_TEXT) || aptr->body->noconv) ? 'n' : 'c');
305  }
306  else if ((aptr->body->type != TYPE_TEXT) || aptr->body->noconv)
307  optional = false;
308  break;
309  case 'd':
310  {
311  const char *const c_message_format =
312  cs_subset_string(NeoMutt->sub, "message_format");
313  if (!optional)
314  {
315  if (aptr->body->description)
316  {
317  mutt_format_s(buf, buflen, prec, aptr->body->description);
318  break;
319  }
320  if (mutt_is_message_type(aptr->body->type, aptr->body->subtype) &&
321  c_message_format && aptr->body->email)
322  {
323  char s[128];
324  mutt_make_string(s, sizeof(s), cols, c_message_format, NULL, -1,
325  aptr->body->email,
327  if (*s)
328  {
329  mutt_format_s(buf, buflen, prec, s);
330  break;
331  }
332  }
333  if (!aptr->body->d_filename && !aptr->body->filename)
334  {
335  mutt_format_s(buf, buflen, prec, "<no description>");
336  break;
337  }
338  }
339  else if (aptr->body->description ||
340  (mutt_is_message_type(aptr->body->type, aptr->body->subtype) &&
341  c_message_format && aptr->body->email))
342  {
343  break;
344  }
345  }
346  /* fallthrough */
347  case 'F':
348  if (!optional)
349  {
350  if (aptr->body->d_filename)
351  {
352  mutt_format_s(buf, buflen, prec, aptr->body->d_filename);
353  break;
354  }
355  }
356  else if (!aptr->body->d_filename && !aptr->body->filename)
357  {
358  optional = false;
359  break;
360  }
361  /* fallthrough */
362  case 'f':
363  if (!optional)
364  {
365  if (aptr->body->filename && (*aptr->body->filename == '/'))
366  {
367  struct Buffer *path = mutt_buffer_pool_get();
368 
369  mutt_buffer_strcpy(path, aptr->body->filename);
371  mutt_format_s(buf, buflen, prec, mutt_buffer_string(path));
373  }
374  else
375  mutt_format_s(buf, buflen, prec, NONULL(aptr->body->filename));
376  }
377  else if (!aptr->body->filename)
378  optional = false;
379  break;
380  case 'D':
381  if (!optional)
382  snprintf(buf, buflen, "%c", aptr->body->deleted ? 'D' : ' ');
383  else if (!aptr->body->deleted)
384  optional = false;
385  break;
386  case 'e':
387  if (!optional)
388  mutt_format_s(buf, buflen, prec, ENCODING(aptr->body->encoding));
389  break;
390  case 'I':
391  if (optional)
392  break;
393 
394  const char dispchar[] = { 'I', 'A', 'F', '-' };
395  char ch;
396 
397  if (aptr->body->disposition < sizeof(dispchar))
398  ch = dispchar[aptr->body->disposition];
399  else
400  {
401  mutt_debug(LL_DEBUG1, "ERROR: invalid content-disposition %d\n",
402  aptr->body->disposition);
403  ch = '!';
404  }
405  snprintf(buf, buflen, "%c", ch);
406  break;
407  case 'm':
408  if (!optional)
409  mutt_format_s(buf, buflen, prec, TYPE(aptr->body));
410  break;
411  case 'M':
412  if (!optional)
413  mutt_format_s(buf, buflen, prec, aptr->body->subtype);
414  else if (!aptr->body->subtype)
415  optional = false;
416  break;
417  case 'n':
418  if (optional)
419  break;
420 
421  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
422  snprintf(buf, buflen, fmt, aptr->num + 1);
423  break;
424  case 'Q':
425  if (optional)
426  optional = aptr->body->attach_qualifies;
427  else
428  {
429  snprintf(fmt, sizeof(fmt), "%%%sc", prec);
430  mutt_format_s(buf, buflen, fmt, "Q");
431  }
432  break;
433  case 's':
434  {
435  size_t l;
436  if (aptr->body->filename && (flags & MUTT_FORMAT_STAT_FILE))
437  {
438  struct stat st;
439  stat(aptr->body->filename, &st);
440  l = st.st_size;
441  }
442  else
443  l = aptr->body->length;
444 
445  if (!optional)
446  {
447  char tmp[128];
448  mutt_str_pretty_size(tmp, sizeof(tmp), l);
449  mutt_format_s(buf, buflen, prec, tmp);
450  }
451  else if (l == 0)
452  optional = false;
453 
454  break;
455  }
456  case 't':
457  if (!optional)
458  snprintf(buf, buflen, "%c", aptr->body->tagged ? '*' : ' ');
459  else if (!aptr->body->tagged)
460  optional = false;
461  break;
462  case 'T':
463  if (!optional)
464  mutt_format_s_tree(buf, buflen, prec, NONULL(aptr->tree));
465  else if (!aptr->tree)
466  optional = false;
467  break;
468  case 'u':
469  if (!optional)
470  snprintf(buf, buflen, "%c", aptr->body->unlink ? '-' : ' ');
471  else if (!aptr->body->unlink)
472  optional = false;
473  break;
474  case 'X':
475  if (optional)
476  optional = ((aptr->body->attach_count + aptr->body->attach_qualifies) != 0);
477  else
478  {
479  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
480  snprintf(buf, buflen, fmt, aptr->body->attach_count + aptr->body->attach_qualifies);
481  }
482  break;
483  default:
484  *buf = '\0';
485  }
486 
487  if (optional)
488  {
489  mutt_expando_format(buf, buflen, col, cols, if_str, attach_format_str, data,
491  }
492  else if (flags & MUTT_FORMAT_OPTIONAL)
493  {
494  mutt_expando_format(buf, buflen, col, cols, else_str, attach_format_str,
495  data, MUTT_FORMAT_NO_FLAGS);
496  }
497 
498  /* We return the format string, unchanged */
499  return src;
500 }
501 
505 static void attach_make_entry(struct Menu *menu, char *buf, size_t buflen, int line)
506 {
507  struct AttachCtx *actx = menu->mdata;
508 
509  const char *const c_attach_format =
510  cs_subset_string(NeoMutt->sub, "attach_format");
511  mutt_expando_format(buf, buflen, 0, menu->win->state.cols, NONULL(c_attach_format),
512  attach_format_str, (intptr_t) (actx->idx[actx->v2r[line]]),
514 }
515 
519 int attach_tag(struct Menu *menu, int sel, int act)
520 {
521  struct AttachCtx *actx = menu->mdata;
522  struct Body *cur = actx->idx[actx->v2r[sel]]->body;
523  bool ot = cur->tagged;
524 
525  cur->tagged = ((act >= 0) ? act : !cur->tagged);
526  return cur->tagged - ot;
527 }
528 
533 static void prepend_savedir(struct Buffer *buf)
534 {
535  if (!buf || !buf->data || (buf->data[0] == '/'))
536  return;
537 
538  struct Buffer *tmp = mutt_buffer_pool_get();
539  const char *const c_attach_save_dir =
540  cs_subset_path(NeoMutt->sub, "attach_save_dir");
541  if (c_attach_save_dir)
542  {
543  mutt_buffer_addstr(tmp, c_attach_save_dir);
544  if (tmp->dptr[-1] != '/')
545  mutt_buffer_addch(tmp, '/');
546  }
547  else
548  mutt_buffer_addstr(tmp, "./");
549 
551  mutt_buffer_copy(buf, tmp);
553 }
554 
560 static bool has_a_message(struct Body *body)
561 {
562  return (body->email && (body->encoding != ENC_BASE64) &&
563  (body->encoding != ENC_QUOTED_PRINTABLE) &&
564  mutt_is_message_type(body->type, body->subtype));
565 }
566 
592 static int save_attachment_flowed_helper(FILE *fp, struct Body *b, const char *path,
593  enum SaveAttach flags, struct Email *e)
594 {
595  int rc = -1;
596 
598  {
599  struct Body b_fake = { 0 };
600 
601  struct Buffer *tempfile = mutt_buffer_pool_get();
602  mutt_buffer_mktemp(tempfile);
603 
604  /* Pass MUTT_SAVE_NO_FLAGS to force mutt_file_fopen("w") */
606  if (rc != 0)
607  goto cleanup;
608 
610 
611  /* Now "really" save it. Send mode does this without touching anything,
612  * so force send-mode. */
613  memset(&b_fake, 0, sizeof(struct Body));
614  b_fake.filename = tempfile->data;
615  rc = mutt_save_attachment(NULL, &b_fake, path, flags, e);
616 
618 
619  cleanup:
620  mutt_buffer_pool_release(&tempfile);
621  }
622  else
623  {
624  rc = mutt_save_attachment(fp, b, path, flags, e);
625  }
626 
627  return rc;
628 }
629 
639 static int query_save_attachment(FILE *fp, struct Body *body, struct Email *e, char **directory)
640 {
641  char *prompt = NULL;
642  enum SaveAttach opt = MUTT_SAVE_NO_FLAGS;
643  int rc = -1;
644 
645  struct Buffer *buf = mutt_buffer_pool_get();
646  struct Buffer *tfile = mutt_buffer_pool_get();
647 
648  if (body->filename)
649  {
650  if (directory && *directory)
651  {
652  mutt_buffer_concat_path(buf, *directory, mutt_path_basename(body->filename));
653  }
654  else
655  mutt_buffer_strcpy(buf, body->filename);
656  }
657  else if (has_a_message(body))
658  {
659  mutt_default_save(buf->data, buf->dsize, body->email);
661  }
662 
663  prepend_savedir(buf);
664 
665  prompt = _("Save to file: ");
666  while (prompt)
667  {
668  if ((mutt_buffer_get_field(prompt, buf, MUTT_FILE | MUTT_CLEAR, false, NULL,
669  NULL, NULL) != 0) ||
671  {
672  goto cleanup;
673  }
674 
675  prompt = NULL;
677 
678  bool is_message = (fp && has_a_message(body));
679 
680  if (is_message)
681  {
682  struct stat st;
683 
684  /* check to make sure that this file is really the one the user wants */
685  rc = mutt_save_confirm(mutt_buffer_string(buf), &st);
686  if (rc == 1)
687  {
688  prompt = _("Save to file: ");
689  continue;
690  }
691  else if (rc == -1)
692  goto cleanup;
693  mutt_buffer_copy(tfile, buf);
694  }
695  else
696  {
697  rc = mutt_check_overwrite(body->filename, mutt_buffer_string(buf), tfile,
698  &opt, directory);
699  if (rc == -1)
700  goto cleanup;
701  else if (rc == 1)
702  {
703  prompt = _("Save to file: ");
704  continue;
705  }
706  }
707 
708  mutt_message(_("Saving..."));
709  if (save_attachment_flowed_helper(fp, body, mutt_buffer_string(tfile), opt,
710  (e || !is_message) ? e : body->email) == 0)
711  {
712  // This uses ngettext to avoid duplication of messages
713  mutt_message(ngettext("Attachment saved", "%d attachments saved", 1), 1);
714  rc = 0;
715  goto cleanup;
716  }
717  else
718  {
719  prompt = _("Save to file: ");
720  continue;
721  }
722  }
723 
724 cleanup:
726  mutt_buffer_pool_release(&tfile);
727  return rc;
728 }
729 
738 static int save_without_prompting(FILE *fp, struct Body *body, struct Email *e)
739 {
740  enum SaveAttach opt = MUTT_SAVE_NO_FLAGS;
741  int rc = -1;
742  struct Buffer *buf = mutt_buffer_pool_get();
743  struct Buffer *tfile = mutt_buffer_pool_get();
744 
745  if (body->filename)
746  {
747  mutt_buffer_strcpy(buf, body->filename);
748  }
749  else if (has_a_message(body))
750  {
751  mutt_default_save(buf->data, buf->dsize, body->email);
752  }
753 
754  prepend_savedir(buf);
756 
757  bool is_message = (fp && has_a_message(body));
758 
759  if (is_message)
760  {
761  mutt_buffer_copy(tfile, buf);
762  }
763  else
764  {
765  rc = mutt_check_overwrite(body->filename, mutt_buffer_string(buf), tfile, &opt, NULL);
766  if (rc == -1) // abort or cancel
767  goto cleanup;
768  }
769 
770  rc = save_attachment_flowed_helper(fp, body, mutt_buffer_string(tfile), opt,
771  (e || !is_message) ? e : body->email);
772 
773 cleanup:
775  mutt_buffer_pool_release(&tfile);
776  return rc;
777 }
778 
788 void mutt_save_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag,
789  struct Body *top, struct Email *e, struct Menu *menu)
790 {
791  char *directory = NULL;
792  int rc = 1;
793  int last = menu_get_index(menu);
794  FILE *fp_out = NULL;
795  int saved_attachments = 0;
796 
797  struct Buffer *buf = mutt_buffer_pool_get();
798  struct Buffer *tfile = mutt_buffer_pool_get();
799 
800  const bool c_attach_split = cs_subset_bool(NeoMutt->sub, "attach_split");
801  const char *const c_attach_sep = cs_subset_string(NeoMutt->sub, "attach_sep");
802  const bool c_attach_save_without_prompting =
803  cs_subset_bool(NeoMutt->sub, "attach_save_without_prompting");
804 
805  for (int i = 0; !tag || (i < actx->idxlen); i++)
806  {
807  if (tag)
808  {
809  fp = actx->idx[i]->fp;
810  top = actx->idx[i]->body;
811  }
812  if (!tag || top->tagged)
813  {
814  if (!c_attach_split)
815  {
816  if (mutt_buffer_is_empty(buf))
817  {
818  enum SaveAttach opt = MUTT_SAVE_NO_FLAGS;
819 
821  prepend_savedir(buf);
822 
823  if ((mutt_buffer_get_field(_("Save to file: "), buf, MUTT_FILE | MUTT_CLEAR,
824  false, NULL, NULL, NULL) != 0) ||
826  {
827  goto cleanup;
828  }
830  if (mutt_check_overwrite(top->filename, mutt_buffer_string(buf), tfile, &opt, NULL))
831  goto cleanup;
832  rc = save_attachment_flowed_helper(fp, top, mutt_buffer_string(tfile), opt, e);
833  if ((rc == 0) && c_attach_sep && (fp_out = fopen(mutt_buffer_string(tfile), "a")))
834  {
835  fprintf(fp_out, "%s", c_attach_sep);
836  mutt_file_fclose(&fp_out);
837  }
838  }
839  else
840  {
842  MUTT_SAVE_APPEND, e);
843  if ((rc == 0) && c_attach_sep && (fp_out = fopen(mutt_buffer_string(tfile), "a")))
844  {
845  fprintf(fp_out, "%s", c_attach_sep);
846  mutt_file_fclose(&fp_out);
847  }
848  }
849  }
850  else
851  {
852  if (tag && menu && top->aptr)
853  {
854  menu_set_index(menu, top->aptr->num);
856 
857  menu_redraw(menu);
858  }
859  if (c_attach_save_without_prompting)
860  {
861  // Save each file, with no prompting, using the configured 'AttachSaveDir'
862  rc = save_without_prompting(fp, top, e);
863  if (rc == 0)
864  saved_attachments++;
865  }
866  else
867  {
868  // Save each file, prompting the user for the location each time.
869  if (query_save_attachment(fp, top, e, &directory) == -1)
870  break;
871  }
872  }
873  }
874  if (!tag)
875  break;
876  }
877 
878  FREE(&directory);
879 
880  if (tag && menu)
881  {
882  menu_set_index(menu, last);
884  }
885 
886  if (rc == 0)
887  {
888  if (!c_attach_split)
889  saved_attachments = 1;
890 
891  if (!c_attach_split || c_attach_save_without_prompting)
892  {
893  mutt_message(ngettext("Attachment saved", "%d attachments saved", saved_attachments),
894  saved_attachments);
895  }
896  }
897 
898 cleanup:
900  mutt_buffer_pool_release(&tfile);
901 }
902 
910 static void query_pipe_attachment(const char *command, FILE *fp, struct Body *body, bool filter)
911 {
912  char tfile[PATH_MAX];
913 
914  if (filter)
915  {
916  char warning[PATH_MAX + 256];
917  snprintf(warning, sizeof(warning),
918  _("WARNING! You are about to overwrite %s, continue?"), body->filename);
919  if (mutt_yesorno(warning, MUTT_NO) != MUTT_YES)
920  {
922  return;
923  }
924  mutt_mktemp(tfile, sizeof(tfile));
925  }
926  else
927  tfile[0] = '\0';
928 
929  if (mutt_pipe_attachment(fp, body, command, tfile))
930  {
931  if (filter)
932  {
933  mutt_file_unlink(body->filename);
934  mutt_file_rename(tfile, body->filename);
936  mutt_message(_("Attachment filtered"));
937  }
938  }
939  else
940  {
941  if (filter && tfile[0])
942  mutt_file_unlink(tfile);
943  }
944 }
945 
952 static void pipe_attachment(FILE *fp, struct Body *b, struct State *state)
953 {
954  if (!state || !state->fp_out)
955  return;
956 
957  FILE *fp_in = NULL;
958  FILE *fp_unstuff = NULL;
959  bool is_flowed = false, unlink_unstuff = false;
960  struct Buffer *unstuff_tempfile = NULL;
961 
963  {
964  is_flowed = true;
965  unstuff_tempfile = mutt_buffer_pool_get();
966  mutt_buffer_mktemp(unstuff_tempfile);
967  }
968 
969  if (fp)
970  {
971  state->fp_in = fp;
972 
973  if (is_flowed)
974  {
975  fp_unstuff = mutt_file_fopen(mutt_buffer_string(unstuff_tempfile), "w");
976  if (fp_unstuff == NULL)
977  {
978  mutt_perror("mutt_file_fopen");
979  goto bail;
980  }
981  unlink_unstuff = true;
982 
983  FILE *filter_fp = state->fp_out;
984  state->fp_out = fp_unstuff;
985  mutt_decode_attachment(b, state);
986  mutt_file_fclose(&fp_unstuff);
987  state->fp_out = filter_fp;
988 
989  fp_unstuff = mutt_file_fopen(mutt_buffer_string(unstuff_tempfile), "r");
990  if (fp_unstuff == NULL)
991  {
992  mutt_perror("mutt_file_fopen");
993  goto bail;
994  }
995  mutt_file_copy_stream(fp_unstuff, filter_fp);
996  mutt_file_fclose(&fp_unstuff);
997  }
998  else
999  mutt_decode_attachment(b, state);
1000  }
1001  else
1002  {
1003  const char *infile = NULL;
1004 
1005  if (is_flowed)
1006  {
1007  if (mutt_save_attachment(fp, b, mutt_buffer_string(unstuff_tempfile), 0, NULL) == -1)
1008  goto bail;
1009  unlink_unstuff = true;
1011  infile = mutt_buffer_string(unstuff_tempfile);
1012  }
1013  else
1014  infile = b->filename;
1015 
1016  fp_in = fopen(infile, "r");
1017  if (!fp_in)
1018  {
1019  mutt_perror("fopen");
1020  goto bail;
1021  }
1022  mutt_file_copy_stream(fp_in, state->fp_out);
1023  mutt_file_fclose(&fp_in);
1024  }
1025 
1026  const char *const c_attach_sep = cs_subset_string(NeoMutt->sub, "attach_sep");
1027  if (c_attach_sep)
1028  state_puts(state, c_attach_sep);
1029 
1030 bail:
1031  mutt_file_fclose(&fp_unstuff);
1032  mutt_file_fclose(&fp_in);
1033 
1034  if (unlink_unstuff)
1035  mutt_file_unlink(mutt_buffer_string(unstuff_tempfile));
1036  mutt_buffer_pool_release(&unstuff_tempfile);
1037 }
1038 
1049 static void pipe_attachment_list(const char *command, struct AttachCtx *actx,
1050  FILE *fp, bool tag, struct Body *top,
1051  bool filter, struct State *state)
1052 {
1053  for (int i = 0; !tag || (i < actx->idxlen); i++)
1054  {
1055  if (tag)
1056  {
1057  fp = actx->idx[i]->fp;
1058  top = actx->idx[i]->body;
1059  }
1060  if (!tag || top->tagged)
1061  {
1062  const bool c_attach_split = cs_subset_bool(NeoMutt->sub, "attach_split");
1063  if (!filter && !c_attach_split)
1064  pipe_attachment(fp, top, state);
1065  else
1066  query_pipe_attachment(command, fp, top, filter);
1067  }
1068  if (!tag)
1069  break;
1070  }
1071 }
1072 
1081 void mutt_pipe_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag,
1082  struct Body *top, bool filter)
1083 {
1084  struct State state = { 0 };
1085  struct Buffer *buf = NULL;
1086 
1087  if (fp)
1088  filter = false; /* sanity check: we can't filter in the recv case yet */
1089 
1090  buf = mutt_buffer_pool_get();
1091  /* perform charset conversion on text attachments when piping */
1092  state.flags = MUTT_CHARCONV;
1093 
1094  if (mutt_buffer_get_field((filter ? _("Filter through: ") : _("Pipe to: ")),
1095  buf, MUTT_CMD, false, NULL, NULL, NULL) != 0)
1096  {
1097  goto cleanup;
1098  }
1099 
1100  if (mutt_buffer_len(buf) == 0)
1101  goto cleanup;
1102 
1104 
1105  const bool c_attach_split = cs_subset_bool(NeoMutt->sub, "attach_split");
1106  if (!filter && !c_attach_split)
1107  {
1108  mutt_endwin();
1109  pid_t pid = filter_create(mutt_buffer_string(buf), &state.fp_out, NULL, NULL);
1110  pipe_attachment_list(mutt_buffer_string(buf), actx, fp, tag, top, filter, &state);
1111  mutt_file_fclose(&state.fp_out);
1112  const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
1113  if ((filter_wait(pid) != 0) || c_wait_key)
1115  }
1116  else
1117  pipe_attachment_list(mutt_buffer_string(buf), actx, fp, tag, top, filter, &state);
1118 
1119 cleanup:
1121 }
1122 
1130 static bool can_print(struct AttachCtx *actx, struct Body *top, bool tag)
1131 {
1132  char type[256];
1133 
1134  for (int i = 0; !tag || (i < actx->idxlen); i++)
1135  {
1136  if (tag)
1137  top = actx->idx[i]->body;
1138  snprintf(type, sizeof(type), "%s/%s", TYPE(top), top->subtype);
1139  if (!tag || top->tagged)
1140  {
1141  if (!mailcap_lookup(top, type, sizeof(type), NULL, MUTT_MC_PRINT))
1142  {
1143  if (!mutt_istr_equal("text/plain", top->subtype) &&
1144  !mutt_istr_equal("application/postscript", top->subtype))
1145  {
1146  if (!mutt_can_decode(top))
1147  {
1148  /* L10N: s gets replaced by a MIME type, e.g. "text/plain" or
1149  application/octet-stream. */
1150  mutt_error(_("I don't know how to print %s attachments"), type);
1151  return false;
1152  }
1153  }
1154  }
1155  }
1156  if (!tag)
1157  break;
1158  }
1159  return true;
1160 }
1161 
1170 static void print_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag,
1171  struct Body *top, struct State *state)
1172 {
1173  char type[256];
1174 
1175  for (int i = 0; !tag || (i < actx->idxlen); i++)
1176  {
1177  if (tag)
1178  {
1179  fp = actx->idx[i]->fp;
1180  top = actx->idx[i]->body;
1181  }
1182  if (!tag || top->tagged)
1183  {
1184  snprintf(type, sizeof(type), "%s/%s", TYPE(top), top->subtype);
1185  const bool c_attach_split = cs_subset_bool(NeoMutt->sub, "attach_split");
1186  if (!c_attach_split && !mailcap_lookup(top, type, sizeof(type), NULL, MUTT_MC_PRINT))
1187  {
1188  if (mutt_istr_equal("text/plain", top->subtype) ||
1189  mutt_istr_equal("application/postscript", top->subtype))
1190  {
1191  pipe_attachment(fp, top, state);
1192  }
1193  else if (mutt_can_decode(top))
1194  {
1195  /* decode and print */
1196 
1197  FILE *fp_in = NULL;
1198  struct Buffer *newfile = mutt_buffer_pool_get();
1199 
1200  mutt_buffer_mktemp(newfile);
1201  if (mutt_decode_save_attachment(fp, top, mutt_buffer_string(newfile),
1203  {
1204  if (!state->fp_out)
1205  {
1206  mutt_error(
1207  "BUG in print_attachment_list(). Please report this. ");
1208  return;
1209  }
1210 
1211  fp_in = fopen(mutt_buffer_string(newfile), "r");
1212  if (fp_in)
1213  {
1214  mutt_file_copy_stream(fp_in, state->fp_out);
1215  mutt_file_fclose(&fp_in);
1216  const char *const c_attach_sep = cs_subset_string(NeoMutt->sub, "attach_sep");
1217  if (c_attach_sep)
1218  state_puts(state, c_attach_sep);
1219  }
1220  }
1222  mutt_buffer_pool_release(&newfile);
1223  }
1224  }
1225  else
1226  mutt_print_attachment(fp, top);
1227  }
1228  if (!tag)
1229  break;
1230  }
1231 }
1232 
1240 void mutt_print_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top)
1241 {
1242  char prompt[128];
1243  struct State state = { 0 };
1244  int tagmsgcount = 0;
1245 
1246  if (tag)
1247  for (int i = 0; i < actx->idxlen; i++)
1248  if (actx->idx[i]->body->tagged)
1249  tagmsgcount++;
1250 
1251  snprintf(prompt, sizeof(prompt),
1252  /* L10N: Although we now the precise number of tagged messages, we
1253  do not show it to the user. So feel free to use a "generic
1254  plural" as plural translation if your language has one. */
1255  tag ? ngettext("Print tagged attachment?", "Print %d tagged attachments?", tagmsgcount) :
1256  _("Print attachment?"),
1257  tagmsgcount);
1258  const enum QuadOption c_print = cs_subset_quad(NeoMutt->sub, "print");
1259  if (query_quadoption(c_print, prompt) != MUTT_YES)
1260  return;
1261 
1262  const bool c_attach_split = cs_subset_bool(NeoMutt->sub, "attach_split");
1263  if (c_attach_split)
1264  {
1265  print_attachment_list(actx, fp, tag, top, &state);
1266  }
1267  else
1268  {
1269  if (!can_print(actx, top, tag))
1270  return;
1271  mutt_endwin();
1272  const char *const c_print_command =
1273  cs_subset_string(NeoMutt->sub, "print_command");
1274  pid_t pid = filter_create(NONULL(c_print_command), &state.fp_out, NULL, NULL);
1275  print_attachment_list(actx, fp, tag, top, &state);
1276  mutt_file_fclose(&state.fp_out);
1277  const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
1278  if ((filter_wait(pid) != 0) || c_wait_key)
1280  }
1281 }
1282 
1288 static void recvattach_extract_pgp_keys(struct AttachCtx *actx, struct Menu *menu)
1289 {
1290  if (!menu->tagprefix)
1291  {
1292  struct AttachPtr *cur_att = current_attachment(actx, menu);
1293  crypt_pgp_extract_key_from_attachment(cur_att->fp, cur_att->body);
1294  }
1295  else
1296  {
1297  for (int i = 0; i < actx->idxlen; i++)
1298  {
1299  if (actx->idx[i]->body->tagged)
1300  {
1301  crypt_pgp_extract_key_from_attachment(actx->idx[i]->fp, actx->idx[i]->body);
1302  }
1303  }
1304  }
1305 }
1306 
1315 static int recvattach_pgp_check_traditional(struct AttachCtx *actx, struct Menu *menu)
1316 {
1317  int rc = 0;
1318 
1319  if (!menu->tagprefix)
1320  {
1321  struct AttachPtr *cur_att = current_attachment(actx, menu);
1322  rc = crypt_pgp_check_traditional(cur_att->fp, cur_att->body, true);
1323  }
1324  else
1325  {
1326  for (int i = 0; i < actx->idxlen; i++)
1327  if (actx->idx[i]->body->tagged)
1328  rc = rc || crypt_pgp_check_traditional(actx->idx[i]->fp, actx->idx[i]->body, true);
1329  }
1330 
1331  return rc;
1332 }
1333 
1341 static void recvattach_edit_content_type(struct ConfigSubset *sub, struct AttachCtx *actx,
1342  struct Menu *menu, struct Email *e)
1343 {
1344  struct AttachPtr *cur_att = current_attachment(actx, menu);
1345  if (!mutt_edit_content_type(e, cur_att->body, cur_att->fp))
1346  return;
1347 
1348  /* The mutt_update_recvattach_menu() will overwrite any changes
1349  * made to a decrypted cur_att->body, so warn the user. */
1350  if (cur_att->decrypted)
1351  {
1352  mutt_message(
1353  _("Structural changes to decrypted attachments are not supported"));
1354  mutt_sleep(1);
1355  }
1356  /* Editing the content type can rewrite the body structure. */
1357  for (int i = 0; i < actx->idxlen; i++)
1358  actx->idx[i]->body = NULL;
1359  mutt_actx_entries_free(actx);
1360  mutt_update_recvattach_menu(sub, actx, menu, true);
1361 }
1362 
1373 int mutt_attach_display_loop(struct ConfigSubset *sub, struct Menu *menu, int op,
1374  struct Email *e, struct AttachCtx *actx, bool recv)
1375 {
1376  do
1377  {
1378  switch (op)
1379  {
1380  case OP_DISPLAY_HEADERS:
1381  bool_str_toggle(NeoMutt->sub, "weed", NULL);
1382  /* fallthrough */
1383 
1384  case OP_VIEW_ATTACH:
1385  {
1386  struct AttachPtr *cur_att = current_attachment(actx, menu);
1387  op = mutt_view_attachment(cur_att->fp, cur_att->body, MUTT_VA_REGULAR,
1388  e, actx, menu->win);
1389  break;
1390  }
1391 
1392  case OP_NEXT_ENTRY:
1393  case OP_MAIN_NEXT_UNDELETED: /* hack */
1394  {
1395  const int index = menu_get_index(menu) + 1;
1396  if (index < menu->max)
1397  {
1398  menu_set_index(menu, index);
1399  op = OP_VIEW_ATTACH;
1400  }
1401  else
1402  op = OP_NULL;
1403  break;
1404  }
1405 
1406  case OP_PREV_ENTRY:
1407  case OP_MAIN_PREV_UNDELETED: /* hack */
1408  {
1409  const int index = menu_get_index(menu) - 1;
1410  if (index >= 0)
1411  {
1412  menu_set_index(menu, index);
1413  op = OP_VIEW_ATTACH;
1414  }
1415  else
1416  op = OP_NULL;
1417  break;
1418  }
1419 
1420  case OP_EDIT_TYPE:
1421  {
1422  struct AttachPtr *cur_att = current_attachment(actx, menu);
1423  /* when we edit the content-type, we should redisplay the attachment
1424  * immediately */
1425  mutt_edit_content_type(e, cur_att->body, cur_att->fp);
1426  if (recv)
1427  recvattach_edit_content_type(sub, actx, menu, e);
1428  else
1429  mutt_edit_content_type(e, cur_att->body, cur_att->fp);
1430 
1432  op = OP_VIEW_ATTACH;
1433  break;
1434  }
1435  /* functions which are passed through from the pager */
1436  case OP_CHECK_TRADITIONAL:
1438  {
1439  op = OP_NULL;
1440  break;
1441  }
1442  /* fallthrough */
1443  case OP_ATTACH_COLLAPSE:
1444  if (recv)
1445  return op;
1446  /* fallthrough */
1447  default:
1448  op = OP_NULL;
1449  }
1450  } while (op != OP_NULL);
1451 
1452  return op;
1453 }
1454 
1465 void mutt_generate_recvattach_list(struct AttachCtx *actx, struct Email *e,
1466  struct Body *parts, FILE *fp,
1467  int parent_type, int level, bool decrypted)
1468 {
1469  struct Body *m = NULL;
1470  struct Body *new_body = NULL;
1471  FILE *fp_new = NULL;
1473  int need_secured, secured;
1474 
1475  for (m = parts; m; m = m->next)
1476  {
1477  need_secured = 0;
1478  secured = 0;
1479 
1480  if (((WithCrypto & APPLICATION_SMIME) != 0) && (type = mutt_is_application_smime(m)))
1481  {
1482  need_secured = 1;
1483 
1484  if (type & SEC_ENCRYPT)
1485  {
1486  if (!crypt_valid_passphrase(APPLICATION_SMIME))
1487  goto decrypt_failed;
1488 
1489  if (e->env)
1491  }
1492 
1493  secured = !crypt_smime_decrypt_mime(fp, &fp_new, m, &new_body);
1494  /* If the decrypt/verify-opaque doesn't generate mime output, an empty
1495  * text/plain type will still be returned by mutt_read_mime_header().
1496  * We can't distinguish an actual part from a failure, so only use a
1497  * text/plain that results from a single top-level part. */
1498  if (secured && (new_body->type == TYPE_TEXT) &&
1499  mutt_istr_equal("plain", new_body->subtype) && ((parts != m) || m->next))
1500  {
1501  mutt_body_free(&new_body);
1502  mutt_file_fclose(&fp_new);
1503  goto decrypt_failed;
1504  }
1505 
1506  if (secured && (type & SEC_ENCRYPT))
1507  e->security |= SMIME_ENCRYPT;
1508  }
1509 
1510  if (((WithCrypto & APPLICATION_PGP) != 0) &&
1512  {
1513  need_secured = 1;
1514 
1515  if (!crypt_valid_passphrase(APPLICATION_PGP))
1516  goto decrypt_failed;
1517 
1518  secured = !crypt_pgp_decrypt_mime(fp, &fp_new, m, &new_body);
1519 
1520  if (secured)
1521  e->security |= PGP_ENCRYPT;
1522  }
1523 
1524  if (need_secured && secured)
1525  {
1526  mutt_actx_add_fp(actx, fp_new);
1527  mutt_actx_add_body(actx, new_body);
1528  mutt_generate_recvattach_list(actx, e, new_body, fp_new, parent_type, level, 1);
1529  continue;
1530  }
1531 
1532  decrypt_failed:
1533  /* Fall through and show the original parts if decryption fails */
1534  if (need_secured && !secured)
1535  mutt_error(_("Can't decrypt encrypted message"));
1536 
1537  /* Strip out the top level multipart */
1538  if ((m->type == TYPE_MULTIPART) && m->parts && !need_secured &&
1539  ((parent_type == -1) && !mutt_istr_equal("alternative", m->subtype)))
1540  {
1541  mutt_generate_recvattach_list(actx, e, m->parts, fp, m->type, level, decrypted);
1542  }
1543  else
1544  {
1545  struct AttachPtr *ap = mutt_mem_calloc(1, sizeof(struct AttachPtr));
1546  mutt_actx_add_attach(actx, ap);
1547 
1548  ap->body = m;
1549  ap->fp = fp;
1550  m->aptr = ap;
1551  ap->parent_type = parent_type;
1552  ap->level = level;
1553  ap->decrypted = decrypted;
1554 
1555  if (m->type == TYPE_MULTIPART)
1556  mutt_generate_recvattach_list(actx, e, m->parts, fp, m->type, level + 1, decrypted);
1557  else if (mutt_is_message_type(m->type, m->subtype))
1558  {
1559  mutt_generate_recvattach_list(actx, m->email, m->parts, fp, m->type,
1560  level + 1, decrypted);
1561  e->security |= m->email->security;
1562  }
1563  }
1564  }
1565 }
1566 
1571 void mutt_attach_init(struct AttachCtx *actx)
1572 {
1573  /* Collapse the attachments if '$digest_collapse' is set AND if...
1574  * the outer container is of type 'multipart/digest' */
1575  bool digest = mutt_istr_equal(actx->email->body->subtype, "digest");
1576 
1577  const bool c_digest_collapse =
1578  cs_subset_bool(NeoMutt->sub, "digest_collapse");
1579  for (int i = 0; i < actx->idxlen; i++)
1580  {
1581  actx->idx[i]->body->tagged = false;
1582 
1583  /* OR an inner container is of type 'multipart/digest' */
1584  actx->idx[i]->body->collapsed =
1585  (c_digest_collapse &&
1586  (digest || ((actx->idx[i]->body->type == TYPE_MULTIPART) &&
1587  mutt_istr_equal(actx->idx[i]->body->subtype, "digest"))));
1588  }
1589 }
1590 
1598 static void mutt_update_recvattach_menu(struct ConfigSubset *sub, struct AttachCtx *actx,
1599  struct Menu *menu, bool init)
1600 {
1601  if (init)
1602  {
1603  mutt_generate_recvattach_list(actx, actx->email, actx->email->body,
1604  actx->fp_root, -1, 0, 0);
1605  mutt_attach_init(actx);
1606  menu->mdata = actx;
1607  }
1608 
1609  mutt_update_tree(actx);
1610 
1611  menu->max = actx->vcount;
1612 
1613  const int index = menu_get_index(menu);
1614  if (index >= menu->max)
1615  menu_set_index(menu, menu->max - 1);
1617 }
1618 
1624 static void attach_collapse(struct AttachCtx *actx, struct Menu *menu)
1625 {
1626  int rindex, curlevel;
1627 
1628  struct AttachPtr *cur_att = current_attachment(actx, menu);
1629  cur_att->body->collapsed = !cur_att->body->collapsed;
1630  /* When expanding, expand all the children too */
1631  if (cur_att->body->collapsed)
1632  return;
1633 
1634  curlevel = cur_att->level;
1635  const int index = menu_get_index(menu);
1636  rindex = actx->v2r[index] + 1;
1637 
1638  const bool c_digest_collapse =
1639  cs_subset_bool(NeoMutt->sub, "digest_collapse");
1640  while ((rindex < actx->idxlen) && (actx->idx[rindex]->level > curlevel))
1641  {
1642  if (c_digest_collapse && (actx->idx[rindex]->body->type == TYPE_MULTIPART) &&
1643  mutt_istr_equal(actx->idx[rindex]->body->subtype, "digest"))
1644  {
1645  actx->idx[rindex]->body->collapsed = true;
1646  }
1647  else
1648  {
1649  actx->idx[rindex]->body->collapsed = false;
1650  }
1651  rindex++;
1652  }
1653 }
1654 
1661 {
1662  if ((nc->event_type != NT_CONFIG) || !nc->global_data || !nc->event_data)
1663  return -1;
1664 
1665  struct EventConfig *ev_c = nc->event_data;
1666 
1667  if (!mutt_str_equal(ev_c->name, "attach_format") && !mutt_str_equal(ev_c->name, "message_format"))
1668  return 0;
1669 
1670  struct Menu *menu = nc->global_data;
1672  mutt_debug(LL_DEBUG5, "config done, request WA_RECALC, MENU_REDRAW_FULL\n");
1673 
1674  return 0;
1675 }
1676 
1685 {
1686  if ((nc->event_type != NT_WINDOW) || !nc->global_data || !nc->event_data)
1687  return -1;
1688 
1689  if (nc->event_subtype != NT_WINDOW_DELETE)
1690  return 0;
1691 
1692  struct MuttWindow *win_menu = nc->global_data;
1693  struct EventWindow *ev_w = nc->event_data;
1694  if (ev_w->win != win_menu)
1695  return 0;
1696 
1697  struct Menu *menu = win_menu->wdata;
1698 
1701 
1702  mutt_debug(LL_DEBUG5, "window delete done\n");
1703  return 0;
1704 }
1705 
1714  struct Email *e, FILE *fp)
1715 {
1716  if (!m || !e || !fp)
1717  {
1718  return;
1719  }
1720 
1721  int op = OP_NULL;
1722 
1723  /* make sure we have parsed this message */
1724  mutt_parse_mime_message(m, e, fp);
1726 
1727  struct MuttWindow *dlg = simple_dialog_new(MENU_ATTACH, WT_DLG_ATTACH, AttachHelp);
1728 
1729  struct Menu *menu = dlg->wdata;
1730  menu->make_entry = attach_make_entry;
1731  menu->tag = attach_tag;
1732 
1733  struct MuttWindow *win_menu = menu->win;
1734 
1735  // NT_COLOR is handled by the SimpleDialog
1738 
1739  struct MuttWindow *sbar = window_find_child(dlg, WT_STATUS_BAR);
1740  sbar_set_title(sbar, _("Attachments"));
1741 
1742  struct AttachCtx *actx = mutt_actx_new();
1743  actx->email = e;
1744  actx->fp_root = fp;
1745  mutt_update_recvattach_menu(sub, actx, menu, true);
1746 
1747  while (true)
1748  {
1749  if (op == OP_NULL)
1750  op = menu_loop(menu);
1751  window_redraw(dlg);
1752  if (!m)
1753  return;
1754  switch (op)
1755  {
1756  case OP_ATTACH_VIEW_MAILCAP:
1757  {
1758  struct AttachPtr *cur_att = current_attachment(actx, menu);
1759  mutt_view_attachment(cur_att->fp, cur_att->body, MUTT_VA_MAILCAP, e,
1760  actx, menu->win);
1762  break;
1763  }
1764 
1765  case OP_ATTACH_VIEW_TEXT:
1766  {
1767  struct AttachPtr *cur_att = current_attachment(actx, menu);
1768  mutt_view_attachment(cur_att->fp, cur_att->body, MUTT_VA_AS_TEXT, e,
1769  actx, menu->win);
1771  break;
1772  }
1773 
1774  case OP_ATTACH_VIEW_PAGER:
1775  {
1776  struct AttachPtr *cur_att = current_attachment(actx, menu);
1777  mutt_view_attachment(cur_att->fp, cur_att->body, MUTT_VA_PAGER, e, actx, menu->win);
1779  break;
1780  }
1781 
1782  case OP_DISPLAY_HEADERS:
1783  case OP_VIEW_ATTACH:
1784  op = mutt_attach_display_loop(sub, menu, op, e, actx, true);
1786  continue;
1787 
1788  case OP_ATTACH_COLLAPSE:
1789  {
1790  struct AttachPtr *cur_att = current_attachment(actx, menu);
1791  if (!cur_att->body->parts)
1792  {
1793  mutt_error(_("There are no subparts to show"));
1794  break;
1795  }
1796  attach_collapse(actx, menu);
1797  mutt_update_recvattach_menu(sub, actx, menu, false);
1798  break;
1799  }
1800 
1801  case OP_FORGET_PASSPHRASE:
1803  break;
1804 
1805  case OP_EXTRACT_KEYS:
1807  {
1808  recvattach_extract_pgp_keys(actx, menu);
1810  }
1811  break;
1812 
1813  case OP_CHECK_TRADITIONAL:
1814  if (((WithCrypto & APPLICATION_PGP) != 0) &&
1816  {
1817  e->security = crypt_query(NULL);
1819  }
1820  break;
1821 
1822  case OP_PRINT:
1823  {
1824  struct AttachPtr *cur_att = current_attachment(actx, menu);
1825  mutt_print_attachment_list(actx, cur_att->fp, menu->tagprefix, cur_att->body);
1826  break;
1827  }
1828 
1829  case OP_PIPE:
1830  {
1831  struct AttachPtr *cur_att = current_attachment(actx, menu);
1832  mutt_pipe_attachment_list(actx, cur_att->fp, menu->tagprefix, cur_att->body, false);
1833  break;
1834  }
1835 
1836  case OP_SAVE:
1837  {
1838  struct AttachPtr *cur_att = current_attachment(actx, menu);
1839  mutt_save_attachment_list(actx, cur_att->fp, menu->tagprefix,
1840  cur_att->body, e, menu);
1841 
1842  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
1843  const int index = menu_get_index(menu) + 1;
1844  if (!menu->tagprefix && c_resolve && (index < menu->max))
1845  menu_set_index(menu, index);
1846  break;
1847  }
1848 
1849  case OP_DELETE:
1850  if (check_readonly(m))
1851  break;
1852 
1853 #ifdef USE_POP
1854  if (m->type == MUTT_POP)
1855  {
1856  mutt_flushinp();
1857  mutt_error(_("Can't delete attachment from POP server"));
1858  break;
1859  }
1860 #endif
1861 
1862 #ifdef USE_NNTP
1863  if (m->type == MUTT_NNTP)
1864  {
1865  mutt_flushinp();
1866  mutt_error(_("Can't delete attachment from news server"));
1867  break;
1868  }
1869 #endif
1870 
1871  if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT))
1872  {
1873  mutt_message(_("Deletion of attachments from encrypted messages is "
1874  "unsupported"));
1875  break;
1876  }
1877  if ((WithCrypto != 0) && (e->security & (SEC_SIGN | SEC_PARTSIGN)))
1878  {
1879  mutt_message(_("Deletion of attachments from signed messages may "
1880  "invalidate the signature"));
1881  }
1882  if (!menu->tagprefix)
1883  {
1884  struct AttachPtr *cur_att = current_attachment(actx, menu);
1885  if (cur_att->parent_type == TYPE_MULTIPART)
1886  {
1887  cur_att->body->deleted = true;
1888  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
1889  const int index = menu_get_index(menu) + 1;
1890  if (c_resolve && (index < menu->max))
1891  {
1892  menu_set_index(menu, index);
1893  }
1894  else
1896  }
1897  else
1898  {
1899  mutt_message(
1900  _("Only deletion of multipart attachments is supported"));
1901  }
1902  }
1903  else
1904  {
1905  for (int i = 0; i < menu->max; i++)
1906  {
1907  if (actx->idx[i]->body->tagged)
1908  {
1909  if (actx->idx[i]->parent_type == TYPE_MULTIPART)
1910  {
1911  actx->idx[i]->body->deleted = true;
1913  }
1914  else
1915  {
1916  mutt_message(
1917  _("Only deletion of multipart attachments is supported"));
1918  }
1919  }
1920  }
1921  }
1922  break;
1923 
1924  case OP_UNDELETE:
1925  if (check_readonly(m))
1926  break;
1927  if (!menu->tagprefix)
1928  {
1929  struct AttachPtr *cur_att = current_attachment(actx, menu);
1930  cur_att->body->deleted = false;
1931  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
1932  const int index = menu_get_index(menu) + 1;
1933  if (c_resolve && (index < menu->max))
1934  {
1935  menu_set_index(menu, index);
1936  }
1937  else
1939  }
1940  else
1941  {
1942  for (int i = 0; i < menu->max; i++)
1943  {
1944  if (actx->idx[i]->body->tagged)
1945  {
1946  actx->idx[i]->body->deleted = false;
1948  }
1949  }
1950  }
1951  break;
1952 
1953  case OP_RESEND:
1954  {
1955  if (check_attach())
1956  break;
1957  struct AttachPtr *cur_att = current_attachment(actx, menu);
1958  mutt_attach_resend(cur_att->fp, m, actx,
1959  menu->tagprefix ? NULL : cur_att->body);
1961  break;
1962  }
1963 
1964  case OP_BOUNCE_MESSAGE:
1965  {
1966  if (check_attach())
1967  break;
1968  struct AttachPtr *cur_att = current_attachment(actx, menu);
1969  mutt_attach_bounce(m, cur_att->fp, actx,
1970  menu->tagprefix ? NULL : cur_att->body);
1972  break;
1973  }
1974 
1975  case OP_FORWARD_MESSAGE:
1976  {
1977  if (check_attach())
1978  break;
1979  struct AttachPtr *cur_att = current_attachment(actx, menu);
1980  mutt_attach_forward(cur_att->fp, e, actx,
1981  menu->tagprefix ? NULL : cur_att->body, SEND_NO_FLAGS);
1983  break;
1984  }
1985 
1986 #ifdef USE_NNTP
1987  case OP_FORWARD_TO_GROUP:
1988  {
1989  if (check_attach())
1990  break;
1991  struct AttachPtr *cur_att = current_attachment(actx, menu);
1992  mutt_attach_forward(cur_att->fp, e, actx,
1993  menu->tagprefix ? NULL : cur_att->body, SEND_NEWS);
1995  break;
1996  }
1997 
1998  case OP_FOLLOWUP:
1999  {
2000  if (check_attach())
2001  break;
2002 
2003  const enum QuadOption c_followup_to_poster =
2004  cs_subset_quad(NeoMutt->sub, "followup_to_poster");
2005  struct AttachPtr *cur_att = current_attachment(actx, menu);
2006  if (!cur_att->body->email->env->followup_to ||
2007  !mutt_istr_equal(cur_att->body->email->env->followup_to,
2008  "poster") ||
2009  (query_quadoption(c_followup_to_poster,
2010  _("Reply by mail as poster prefers?")) != MUTT_YES))
2011  {
2012  mutt_attach_reply(cur_att->fp, m, e, actx,
2013  menu->tagprefix ? NULL : cur_att->body, SEND_NEWS | SEND_REPLY);
2015  break;
2016  }
2017  }
2018 #endif
2019  /* fallthrough */
2020  case OP_REPLY:
2021  case OP_GROUP_REPLY:
2022  case OP_GROUP_CHAT_REPLY:
2023  case OP_LIST_REPLY:
2024  {
2025  if (check_attach())
2026  break;
2027 
2028  SendFlags flags = SEND_REPLY;
2029  if (op == OP_GROUP_REPLY)
2030  flags |= SEND_GROUP_REPLY;
2031  else if (op == OP_GROUP_CHAT_REPLY)
2032  flags |= SEND_GROUP_CHAT_REPLY;
2033  else if (op == OP_LIST_REPLY)
2034  flags |= SEND_LIST_REPLY;
2035 
2036  struct AttachPtr *cur_att = current_attachment(actx, menu);
2037  mutt_attach_reply(cur_att->fp, m, e, actx,
2038  menu->tagprefix ? NULL : cur_att->body, flags);
2040  break;
2041  }
2042 
2043  case OP_COMPOSE_TO_SENDER:
2044  {
2045  if (check_attach())
2046  break;
2047  struct AttachPtr *cur_att = current_attachment(actx, menu);
2048  mutt_attach_mail_sender(cur_att->fp, e, actx,
2049  menu->tagprefix ? NULL : cur_att->body);
2051  break;
2052  }
2053 
2054  case OP_EDIT_TYPE:
2055  recvattach_edit_content_type(sub, actx, menu, e);
2057  break;
2058 
2059  case OP_EXIT:
2060  e->attach_del = false;
2061  for (int i = 0; i < actx->idxlen; i++)
2062  {
2063  if (actx->idx[i]->body && actx->idx[i]->body->deleted)
2064  {
2065  e->attach_del = true;
2066  break;
2067  }
2068  }
2069  if (e->attach_del)
2070  e->changed = true;
2071 
2072  mutt_actx_free(&actx);
2073 
2074  simple_dialog_free(&dlg);
2075  return;
2076  }
2077 
2078  op = OP_NULL;
2079  }
2080 
2081  /* not reached */
2082 }
void mutt_parse_mime_message(struct Mailbox *m, struct Email *e, FILE *fp)
Parse a MIME email.
Definition: attachments.c:591
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
Convenience wrapper for the gui headers.
void mutt_decode_attachment(struct Body *b, struct State *s)
Decode an email&#39;s attachment.
Definition: handler.c:1861
struct MuttWindow * window_find_child(struct MuttWindow *win, enum WindowType type)
Recursively find a child Window of a given type.
Definition: mutt_window.c:550
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
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:780
char * filename
when sending a message, this is the file to which this structure refers
Definition: body.h:46
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
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
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:113
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1385
static bool has_a_message(struct Body *body)
Determine if the Body has a message (to save)
Definition: recvattach.c:560
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:58
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
void simple_dialog_free(struct MuttWindow **ptr)
Destroy a simple index Dialog.
Definition: simple.c:165
bool mutt_rfc3676_is_format_flowed(struct Body *b)
Is the Email "format-flowed"?
Definition: rfc3676.c:391
void mutt_update_encoding(struct Body *a, struct ConfigSubset *sub)
Update the encoding type.
Definition: sendlib.c:904
Definition: lib.h:67
Data passed to a notification function.
Definition: observer.h:39
struct MuttWindow * win
Window that changed.
Definition: mutt_window.h:217
struct Body * body
List of MIME parts.
Definition: email.h:91
&#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
An Event that happened to a Window.
Definition: mutt_window.h:215
#define mutt_error(...)
Definition: logging.h:88
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:1465
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
int mutt_buffer_get_field(const char *field, struct Buffer *buf, CompletionFlags complete, bool multiple, struct Mailbox *m, char ***files, int *numfiles)
Ask the user for a string.
Definition: curs_lib.c:268
#define MUTT_FORMAT_FORCESUBJ
Print the subject even if unchanged.
Definition: format_flags.h:31
Attach Dialog, dlg_select_attachment()
Definition: mutt_window.h:78
struct Email * email
Used by recvattach for updating.
Definition: attach.h:51
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition: file.c:1350
#define SMIME_ENCRYPT
Definition: lib.h:99
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:75
No flags set.
Definition: mutt_attach.h:57
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:668
static void attach_make_entry(struct Menu *menu, char *buf, size_t buflen, int line)
Format a menu item for the attachment list - Implements Menu::make_entry() -.
Definition: recvattach.c:505
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:533
void crypt_forget_passphrase(void)
Forget a passphrase and display a message.
Definition: crypt.c:93
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:195
RFC1524 Mailcap routines.
SecurityFlags mutt_is_application_smime(struct Body *b)
Does the message use S/MIME?
Definition: crypt.c:623
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:1170
A config-change event.
Definition: subset.h:69
#define PGP_ENCRYPT
Definition: lib.h:93
FILE * fp_root
Used by recvattach for updating.
Definition: attach.h:52
String manipulation buffer.
Definition: buffer.h:33
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
#define _(a)
Definition: message.h:28
Window is about to be deleted.
Definition: mutt_window.h:206
bool changed
Email has been edited.
Definition: email.h:48
static bool check_readonly(struct Mailbox *m)
Check if the Mailbox is readonly.
Definition: recvattach.c:157
struct Body * next
next attachment in the list
Definition: body.h:53
int menu_redraw(struct Menu *menu)
Redraw the parts of the screen that have been flagged to be redrawn.
Definition: draw.c:546
short idxlen
Number of attachmentes.
Definition: attach.h:55
void window_redraw(struct MuttWindow *win)
Reflow, recalc and repaint a tree of Windows.
Definition: mutt_window.c:632
FILE * fp_out
File to write to.
Definition: state.h:47
Force viewing as text.
Definition: mutt_attach.h:45
uint16_t SecurityFlags
Flags, e.g. SEC_ENCRYPT.
Definition: lib.h:71
#define MUTT_MESSAGE_HOOK
message-hook: run before displaying a message
Definition: hook.h:46
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:639
#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:879
View using default method.
Definition: mutt_attach.h:43
uint16_t SendFlags
Flags for mutt_send_message(), e.g. SEND_REPLY.
Definition: send.h:36
static void mutt_update_recvattach_menu(struct ConfigSubset *sub, struct AttachCtx *actx, struct Menu *menu, bool init)
Update the Attachment Menu.
Definition: recvattach.c:1598
#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:677
Flags to control mutt_expando_format()
All user-callable functions.
void msgwin_clear_text(void)
Clear the text in the Message Window.
Definition: msgwin.c:242
#define mutt_perror(...)
Definition: logging.h:89
Container for Accounts, Notifications.
Definition: neomutt.h:36
FILE * fp_in
File to read from.
Definition: state.h:46
static int attach_window_observer(struct NotifyCallback *nc)
Notification that a Window has changed - Implements observer_t.
Definition: recvattach.c:1684
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:603
MuttWindow has changed, NotifyWindow, EventWindow.
Definition: notify_type.h:53
int mutt_save_confirm(const char *s, struct stat *st)
Ask the user to save.
Definition: muttlib.c:1360
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
The body of an email.
Definition: body.h:34
Status Bar containing extra info about the Index/Pager/etc.
Definition: mutt_window.h:102
Lower left corner.
Definition: mutt_thread.h:42
QuadOption
Possible values for a quad-option.
Definition: quad.h:35
unsigned int disposition
content-disposition, ContentDisposition
Definition: body.h:67
Convenience wrapper for the config headers.
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:475
void(* make_entry)(struct Menu *menu, char *buf, size_t buflen, int line)
Definition: lib.h:105
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:39
int event_subtype
Send: Event subtype, e.g. NT_ACCOUNT_ADD.
Definition: observer.h:43
enum NotifyType event_type
Send: Event type, e.g. NT_ACCOUNT.
Definition: observer.h:42
Some miscellaneous functions.
StateFlags flags
Flags, e.g. MUTT_DISPLAY.
Definition: state.h:49
bool crypt_valid_passphrase(SecurityFlags flags)
Check that we have a usable passphrase, ask if not.
Definition: crypt.c:135
size_t dsize
Length of data.
Definition: buffer.h:37
bool attach_qualifies
This attachment should be counted.
Definition: body.h:84
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1461
struct Notify * notify
Notifications: NotifyWindow, EventWindow.
Definition: mutt_window.h:138
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 -Expando Description %C Character set ...
Definition: recvattach.c:270
Parse and execute user-defined hooks.
SecurityFlags mutt_is_multipart_encrypted(struct Body *b)
Does the message have encrypted parts?
Definition: crypt.c:460
Many unsorted constants and some structs.
Mailcap print field.
Definition: mailcap.h:59
struct MuttWindow * win
Window holding the Menu.
Definition: lib.h:76
bool readonly
Don&#39;t allow changes to the mailbox.
Definition: mailbox.h:119
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
int parent_type
Type of parent attachment, e.g. TYPE_MULTIPART.
Definition: attach.h:38
void mutt_actx_entries_free(struct AttachCtx *actx)
Free entries in an Attachment Context.
Definition: attach.c:103
void mutt_actx_add_body(struct AttachCtx *actx, struct Body *new_body)
Add an email box to an Attachment Context.
Definition: attach.c:83
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:214
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
Select an attachment.
Definition: type.h:38
bool tagged
This attachment is tagged.
Definition: body.h:70
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:66
Base-64 encoded text.
Definition: mime.h:52
bool notify_observer_add(struct Notify *notify, enum NotifyType type, observer_t callback, void *global_data)
Add an observer to an object.
Definition: notify.c:189
void crypt_pgp_extract_key_from_attachment(FILE *fp, struct Body *top)
Wrapper for CryptModuleSpecs::pgp_extract_key_from_attachment()
Definition: cryptglue.c:401
void * global_data
Data from notify_observer_add()
Definition: observer.h:45
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:83
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
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:60
#define MUTT_CMD
Do completion on previous word.
Definition: mutt.h:56
static void mutt_update_v2r(struct AttachCtx *actx)
Update the virtual list of attachments.
Definition: recvattach.c:175
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:88
bool decrypted
Not part of message as stored in the email->body.
Definition: attach.h:43
int mutt_attach_display_loop(struct ConfigSubset *sub, struct Menu *menu, int op, struct Email *e, struct AttachCtx *actx, bool recv)
Event loop for the Attachment menu.
Definition: recvattach.c:1373
bool crypt_pgp_check_traditional(FILE *fp, struct Body *b, bool just_one)
Wrapper for CryptModuleSpecs::pgp_check_traditional()
Definition: cryptglue.c:288
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
A set of inherited config items.
Definition: subset.h:46
#define mutt_mktemp(buf, buflen)
Definition: muttlib.h:71
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:87
static int save_without_prompting(FILE *fp, struct Body *body, struct Email *e)
Save the attachment, without prompting each time.
Definition: recvattach.c:738
void * mdata
Private data.
Definition: lib.h:155
WHERE bool OptAttachMsg
(pseudo) used by attach-message
Definition: options.h:31
&#39;POP3&#39; Mailbox type
Definition: mailbox.h:55
void mutt_endwin(void)
Shutdown curses/slang.
Definition: curs_lib.c:422
void dlg_select_attachment(struct ConfigSubset *sub, struct Mailbox *m, struct Email *e, FILE *fp)
Show the attachments in a Menu.
Definition: recvattach.c:1713
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
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
#define PATH_MAX
Definition: mutt.h:40
static struct AttachPtr * current_attachment(struct AttachCtx *actx, struct Menu *menu)
Get the current attachment.
Definition: recvattach.c:128
Right arrow.
Definition: mutt_thread.h:48
static int attach_config_observer(struct NotifyCallback *nc)
Notification that a Config Variable has changed - Implements observer_t.
Definition: recvattach.c:1660
static void recvattach_edit_content_type(struct ConfigSubset *sub, struct AttachCtx *actx, struct Menu *menu, struct Email *e)
Edit the content type of an attachment.
Definition: recvattach.c:1341
int num
Attachment index number.
Definition: attach.h:41
void mutt_attach_init(struct AttachCtx *actx)
Create a new Attachment context.
Definition: recvattach.c:1571
bool tagprefix
User has pressed <tag-prefix>
Definition: lib.h:75
static void recvattach_extract_pgp_keys(struct AttachCtx *actx, struct Menu *menu)
Extract PGP keys from attachments.
Definition: recvattach.c:1288
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:1009
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:1679
void crypt_smime_getkeys(struct Envelope *env)
Wrapper for CryptModuleSpecs::smime_getkeys()
Definition: cryptglue.c:463
bool mutt_can_decode(struct Body *a)
Will decoding the attachment produce any output.
Definition: handler.c:1821
char * data
Pointer to data.
Definition: buffer.h:35
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:910
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:1049
FILE * fp
Used in the recvattach menu.
Definition: attach.h:37
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
API for encryption/signing of emails.
SecurityFlags mutt_is_malformed_multipart_pgp_encrypted(struct Body *b)
Check for malformed layout.
Definition: crypt.c:521
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:439
void mutt_rfc3676_space_unstuff_attachment(struct Body *b, const char *filename)
Unstuff attachments.
Definition: rfc3676.c:515
Left T-piece.
Definition: mutt_thread.h:44
Ask the user a question.
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:53
static void pipe_attachment(FILE *fp, struct Body *b, struct State *state)
Pipe the attachment to a command.
Definition: recvattach.c:952
Handling of email attachments.
void sbar_set_title(struct MuttWindow *win, const char *title)
Set the title for the Simple Bar.
Definition: sbar.c:221
int(* tag)(struct Menu *menu, int sel, int act)
Definition: lib.h:130
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
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:434
struct Notify * notify
Notifications handler.
Definition: neomutt.h:38
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
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:1315
static bool can_print(struct AttachCtx *actx, struct Body *top, bool tag)
Do we know how to print this attachment type?
Definition: recvattach.c:1130
View attachment in pager using copiousoutput mailcap.
Definition: mutt_attach.h:46
Force viewing using mailcap entry.
Definition: mutt_attach.h:44
#define SEND_REPLY
Reply to sender.
Definition: send.h:40
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition: helpers.c:218
#define MUTT_FILE
Do file completion.
Definition: mutt.h:54
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:180
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
void mutt_actx_add_attach(struct AttachCtx *actx, struct AttachPtr *attach)
Add an Attachment to an Attachment Context.
Definition: attach.c:40
unsigned int type
content-type primary type, ContentType
Definition: body.h:65
void * event_data
Data from notify_send()
Definition: observer.h:44
int max
Number of entries in the menu.
Definition: lib.h:71
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:625
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
void mutt_print_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top)
Print a list of Attachments.
Definition: recvattach.c:1240
void mutt_attach_forward(FILE *fp, struct Email *e, struct AttachCtx *actx, struct Body *cur, SendFlags flags)
Forward an Attachment.
Definition: recvcmd.c:784
Config has changed, NotifyConfig, EventConfig.
Definition: notify_type.h:42
#define SEC_SIGN
Email is signed.
Definition: lib.h:76
int mutt_any_key_to_continue(const char *s)
Prompt the user to &#39;press any key&#39; and wait.
Definition: curs_lib.c:455
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:1624
#define TYPE(body)
Definition: mime.h:89
static bool check_attach(void)
Check if in attach-message mode.
Definition: recvattach.c:140
Horizontal line.
Definition: mutt_thread.h:45
#define SEND_GROUP_REPLY
Reply to all.
Definition: send.h:41
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:212
Type: &#39;multipart/*&#39;.
Definition: mime.h:37
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:322
void mutt_format_s(char *buf, size_t buflen, const char *prec, const char *s)
Format a simple string.
Definition: curs_lib.c:867
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:592
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
struct AttachCtx * mutt_actx_new(void)
Create a new Attachment Context.
Definition: attach.c:131
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition: body.h:69
char * followup_to
List of &#39;followup-to&#39; fields.
Definition: envelope.h:77
Quoted-printable text.
Definition: mime.h:51
void mutt_update_tree(struct AttachCtx *actx)
Refresh the list of attachments.
Definition: recvattach.c:204
struct ConfigSubset * sub
Inherited config items.
Definition: lib.h:78
void mutt_actx_add_fp(struct AttachCtx *actx, FILE *fp_new)
Save a File handle to the Attachment Context.
Definition: attach.c:62
Append to existing file.
Definition: mutt_attach.h:58
SecurityFlags crypt_query(struct Body *b)
Check out the type of encryption used.
Definition: crypt.c:698
#define mutt_message(...)
Definition: logging.h:87
#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:788
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
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:1115
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:38
#define MUTT_FORMAT_OPTIONAL
Allow optional field processing.
Definition: format_flags.h:33
bool notify_observer_remove(struct Notify *notify, observer_t callback, void *global_data)
Remove an observer from an object.
Definition: notify.c:228
struct MuttWindow * simple_dialog_new(enum MenuType mtype, enum WindowType wtype, const struct Mapping *help_data)
Create a simple index Dialog.
Definition: simple.c:128
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:89
Handling of global boolean variables.
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:598
#define SEND_GROUP_CHAT_REPLY
Reply to all recipients preserving To/Cc.
Definition: send.h:52
bool mutt_edit_content_type(struct Email *e, struct Body *b, FILE *fp)
Edit the content type of an attachment.
Definition: commands.c:1433
Log at debug level 5.
Definition: logging.h:44
int attach_tag(struct Menu *menu, int sel, int act)
Tag an attachment - Implements Menu::tag() -.
Definition: recvattach.c:519
Convenience wrapper for the library headers.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
Miscellaneous email parsing routines.
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
Decide how to display email content.
void * wdata
Private data.
Definition: mutt_window.h:145
#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
const char * name
Name of config item that changed.
Definition: subset.h:72
int level
Nesting depth of attachment.
Definition: attach.h:40
struct AttachPtr ** idx
Array of attachments.
Definition: attach.h:54
int mutt_pipe_attachment(FILE *fp, struct Body *b, const char *path, char *outfile)
Pipe an attachment to a command.
Definition: mutt_attach.c:712
#define SEC_PARTSIGN
Not all parts of the email is signed.
Definition: lib.h:79
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:1081
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:879
short vcount
The number of virtual attachments.
Definition: attach.h:59
SaveAttach
Options for saving attachments.
Definition: mutt_attach.h:55
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:39
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:415
int mutt_print_attachment(FILE *fp, struct Body *a)
Print out an attachment.
Definition: mutt_attach.c:1108
void mutt_actx_free(struct AttachCtx **ptr)
Free an Attachment Context.
Definition: attach.c:140
struct Body * body
Attachment.
Definition: attach.h:36