NeoMutt  2022-04-29-145-g9b6a0e
Teaching an old dog new tricks
DOXYGEN
mutt_attach.c
Go to the documentation of this file.
1 
30 #include "config.h"
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <sys/stat.h>
37 #include <sys/wait.h>
38 #include <unistd.h>
39 #include "mutt/lib.h"
40 #include "config/lib.h"
41 #include "email/lib.h"
42 #include "core/lib.h"
43 #include "gui/lib.h"
44 #include "mutt_attach.h"
45 #include "lib.h"
46 #include "ncrypt/lib.h"
47 #include "pager/lib.h"
48 #include "question/lib.h"
49 #include "send/lib.h"
50 #include "cid.h"
51 #include "copy.h"
52 #include "handler.h"
53 #include "mailcap.h"
54 #include "mutt_globals.h"
55 #include "muttlib.h"
56 #include "mx.h"
57 #include "protos.h"
58 #include "rfc3676.h"
59 #ifdef USE_IMAP
60 #include "imap/lib.h"
61 #endif
62 
70 {
71  char type[256];
72 
73  if (a->unlink)
74  return 0;
75 
76  struct Buffer *tmpfile = mutt_buffer_pool_get();
77  struct MailcapEntry *entry = mailcap_entry_new();
78  snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
79  mailcap_lookup(a, type, sizeof(type), entry, MUTT_MC_NO_FLAGS);
80  mailcap_expand_filename(entry->nametemplate, a->filename, tmpfile);
81 
82  mailcap_entry_free(&entry);
83 
84  FILE *fp_in = NULL, *fp_out = NULL;
85  if ((fp_in = fopen(a->filename, "r")) &&
86  (fp_out = mutt_file_fopen(mutt_buffer_string(tmpfile), "w")))
87  {
88  mutt_file_copy_stream(fp_in, fp_out);
90  a->unlink = true;
91 
92  struct stat st = { 0 };
93  if ((fstat(fileno(fp_in), &st) == 0) && (a->stamp >= st.st_mtime))
94  {
96  }
97  }
98  else
99  mutt_perror(fp_in ? mutt_buffer_string(tmpfile) : a->filename);
100 
101  mutt_file_fclose(&fp_in);
102  mutt_file_fclose(&fp_out);
103 
104  mutt_buffer_pool_release(&tmpfile);
105 
106  return a->unlink ? 0 : -1;
107 }
108 
116 {
117  char type[256];
118  struct MailcapEntry *entry = mailcap_entry_new();
119  bool unlink_newfile = false;
120  int rc = 0;
121  struct Buffer *cmd = mutt_buffer_pool_get();
122  struct Buffer *newfile = mutt_buffer_pool_get();
123  struct Buffer *tmpfile = mutt_buffer_pool_get();
124 
125  snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
126  if (mailcap_lookup(a, type, sizeof(type), entry, MUTT_MC_COMPOSE))
127  {
128  if (entry->composecommand || entry->composetypecommand)
129  {
130  if (entry->composetypecommand)
132  else
133  mutt_buffer_strcpy(cmd, entry->composecommand);
134 
135  mailcap_expand_filename(entry->nametemplate, a->filename, newfile);
136  mutt_debug(LL_DEBUG1, "oldfile: %s newfile: %s\n", a->filename,
137  mutt_buffer_string(newfile));
138  if (mutt_file_symlink(a->filename, mutt_buffer_string(newfile)) == -1)
139  {
140  if (mutt_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
141  goto bailout;
142  mutt_buffer_strcpy(newfile, a->filename);
143  }
144  else
145  unlink_newfile = true;
146 
147  if (mailcap_expand_command(a, mutt_buffer_string(newfile), type, cmd))
148  {
149  /* For now, editing requires a file, no piping */
150  mutt_error(_("Mailcap compose entry requires %%s"));
151  }
152  else
153  {
154  int r;
155 
156  mutt_endwin();
158  if (r == -1)
159  mutt_error(_("Error running \"%s\""), mutt_buffer_string(cmd));
160 
161  if ((r != -1) && entry->composetypecommand)
162  {
163  struct Body *b = NULL;
164 
165  FILE *fp = mutt_file_fopen(a->filename, "r");
166  if (!fp)
167  {
168  mutt_perror(_("Failure to open file to parse headers"));
169  goto bailout;
170  }
171 
172  b = mutt_read_mime_header(fp, 0);
173  if (b)
174  {
175  if (!TAILQ_EMPTY(&b->parameter))
176  {
178  a->parameter = b->parameter;
179  TAILQ_INIT(&b->parameter);
180  }
181  if (b->description)
182  {
183  FREE(&a->description);
184  a->description = b->description;
185  b->description = NULL;
186  }
187  if (b->form_name)
188  {
189  FREE(&a->form_name);
190  a->form_name = b->form_name;
191  b->form_name = NULL;
192  }
193 
194  /* Remove headers by copying out data to another file, then
195  * copying the file back */
196  const LOFF_T offset = b->offset;
197  mutt_body_free(&b);
198  if (!mutt_file_seek(fp, offset, SEEK_SET))
199  {
200  goto bailout;
201  }
202 
203  mutt_buffer_mktemp(tmpfile);
204  FILE *fp_tmp = mutt_file_fopen(mutt_buffer_string(tmpfile), "w");
205  if (!fp_tmp)
206  {
207  mutt_perror(_("Failure to open file to strip headers"));
208  mutt_file_fclose(&fp);
209  goto bailout;
210  }
211  mutt_file_copy_stream(fp, fp_tmp);
212  mutt_file_fclose(&fp);
213  mutt_file_fclose(&fp_tmp);
215  if (mutt_file_rename(mutt_buffer_string(tmpfile), a->filename) != 0)
216  {
217  mutt_perror(_("Failure to rename file"));
218  goto bailout;
219  }
220  }
221  }
222  }
223  }
224  }
225  else
226  {
227  mutt_message(_("No mailcap compose entry for %s, creating empty file"), type);
228  rc = 1;
229  goto bailout;
230  }
231 
232  rc = 1;
233 
234 bailout:
235 
236  if (unlink_newfile)
237  unlink(mutt_buffer_string(newfile));
238 
240  mutt_buffer_pool_release(&newfile);
241  mutt_buffer_pool_release(&tmpfile);
242 
243  mailcap_entry_free(&entry);
244  return rc;
245 }
246 
261 {
262  char type[256];
263  struct MailcapEntry *entry = mailcap_entry_new();
264  bool unlink_newfile = false;
265  int rc = 0;
266  struct Buffer *cmd = mutt_buffer_pool_get();
267  struct Buffer *newfile = mutt_buffer_pool_get();
268 
269  snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
270  if (mailcap_lookup(a, type, sizeof(type), entry, MUTT_MC_EDIT))
271  {
272  if (entry->editcommand)
273  {
274  mutt_buffer_strcpy(cmd, entry->editcommand);
275  mailcap_expand_filename(entry->nametemplate, a->filename, newfile);
276  mutt_debug(LL_DEBUG1, "oldfile: %s newfile: %s\n", a->filename,
277  mutt_buffer_string(newfile));
278  if (mutt_file_symlink(a->filename, mutt_buffer_string(newfile)) == -1)
279  {
280  if (mutt_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
281  goto bailout;
282  mutt_buffer_strcpy(newfile, a->filename);
283  }
284  else
285  unlink_newfile = true;
286 
287  if (mailcap_expand_command(a, mutt_buffer_string(newfile), type, cmd))
288  {
289  /* For now, editing requires a file, no piping */
290  mutt_error(_("Mailcap Edit entry requires %%s"));
291  goto bailout;
292  }
293  else
294  {
295  mutt_endwin();
296  if (mutt_system(mutt_buffer_string(cmd)) == -1)
297  {
298  mutt_error(_("Error running \"%s\""), mutt_buffer_string(cmd));
299  goto bailout;
300  }
301  }
302  }
303  }
304  else if (a->type == TYPE_TEXT)
305  {
306  /* On text, default to editor */
307  const char *const c_editor = cs_subset_string(NeoMutt->sub, "editor");
308  mutt_edit_file(NONULL(c_editor), a->filename);
309  }
310  else
311  {
312  mutt_error(_("No mailcap edit entry for %s"), type);
313  rc = 0;
314  goto bailout;
315  }
316 
317  rc = 1;
318 
319 bailout:
320 
321  if (unlink_newfile)
322  unlink(mutt_buffer_string(newfile));
323 
325  mutt_buffer_pool_release(&newfile);
326 
327  mailcap_entry_free(&entry);
328  return rc;
329 }
330 
337 void mutt_check_lookup_list(struct Body *b, char *type, size_t len)
338 {
339  struct ListNode *np = NULL;
340  STAILQ_FOREACH(np, &MimeLookupList, entries)
341  {
342  const int i = mutt_str_len(np->data) - 1;
343  if (((i > 0) && (np->data[i - 1] == '/') && (np->data[i] == '*') &&
344  mutt_istrn_equal(type, np->data, i)) ||
345  mutt_istr_equal(type, np->data))
346  {
347  struct Body tmp = { 0 };
348  enum ContentType n;
349  if ((n = mutt_lookup_mime_type(&tmp, b->filename)) != TYPE_OTHER ||
350  (n = mutt_lookup_mime_type(&tmp, b->description)) != TYPE_OTHER)
351  {
352  snprintf(type, len, "%s/%s",
353  (n == TYPE_AUDIO) ? "audio" :
354  (n == TYPE_APPLICATION) ? "application" :
355  (n == TYPE_IMAGE) ? "image" :
356  (n == TYPE_MESSAGE) ? "message" :
357  (n == TYPE_MODEL) ? "model" :
358  (n == TYPE_MULTIPART) ? "multipart" :
359  (n == TYPE_TEXT) ? "text" :
360  (n == TYPE_VIDEO) ? "video" :
361  "other",
362  tmp.subtype);
363  mutt_debug(LL_DEBUG1, "\"%s\" -> %s\n", b->filename, type);
364  }
365  FREE(&tmp.subtype);
366  FREE(&tmp.xtype);
367  }
368  }
369 }
370 
384 static int wait_interactive_filter(pid_t pid)
385 {
386  int rc;
387 
388 #ifdef USE_IMAP
389  rc = imap_wait_keepalive(pid);
390 #else
391  waitpid(pid, &rc, 0);
392 #endif
394  rc = WIFEXITED(rc) ? WEXITSTATUS(rc) : -1;
395 
396  return rc;
397 }
398 
417 int mutt_view_attachment(FILE *fp, struct Body *a, enum ViewAttachMode mode,
418  struct Email *e, struct AttachCtx *actx, struct MuttWindow *win)
419 {
420  bool use_mailcap = false;
421  bool use_pipe = false;
422  bool use_pager = true;
423  char type[256];
424  char desc[256];
425  char *fname = NULL;
426  struct MailcapEntry *entry = NULL;
427  int rc = -1;
428  bool has_tempfile = false;
429  bool unlink_pagerfile = false;
430 
431  bool is_message = mutt_is_message_type(a->type, a->subtype);
432  if ((WithCrypto != 0) && is_message && a->email &&
434  {
435  return rc;
436  }
437 
438  struct Buffer *tmpfile = mutt_buffer_pool_get();
439  struct Buffer *pagerfile = mutt_buffer_pool_get();
440  struct Buffer *cmd = mutt_buffer_pool_get();
441 
442  use_mailcap = ((mode == MUTT_VA_MAILCAP) ||
443  ((mode == MUTT_VA_REGULAR) && mutt_needs_mailcap(a)) ||
444  (mode == MUTT_VA_PAGER));
445  snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
446 
447  char columns[16];
448  snprintf(columns, sizeof(columns), "%d", win->state.cols);
449  mutt_envlist_set("COLUMNS", columns, true);
450 
451  if (use_mailcap)
452  {
453  entry = mailcap_entry_new();
454  enum MailcapLookup mailcap_opt = (mode == MUTT_VA_PAGER) ? MUTT_MC_AUTOVIEW : MUTT_MC_NO_FLAGS;
455  if (!mailcap_lookup(a, type, sizeof(type), entry, mailcap_opt))
456  {
457  if ((mode == MUTT_VA_REGULAR) || (mode == MUTT_VA_PAGER))
458  {
459  /* fallback to view as text */
460  mailcap_entry_free(&entry);
461  mutt_error(_("No matching mailcap entry found. Viewing as text."));
462  mode = MUTT_VA_AS_TEXT;
463  use_mailcap = false;
464  }
465  else
466  goto return_error;
467  }
468  }
469 
470  if (use_mailcap)
471  {
472  if (!entry->command)
473  {
474  mutt_error(_("MIME type not defined. Can't view attachment."));
475  goto return_error;
476  }
477  mutt_buffer_strcpy(cmd, entry->command);
478 
479  fname = mutt_str_dup(a->filename);
480  /* In send mode(!fp), we allow slashes because those are part of
481  * the tmpfile. The path will be removed in expand_filename */
482  mutt_file_sanitize_filename(fname, fp ? true : false);
483  mailcap_expand_filename(entry->nametemplate, fname, tmpfile);
484  FREE(&fname);
485 
486  if (mutt_save_attachment(fp, a, mutt_buffer_string(tmpfile), 0, NULL) == -1)
487  goto return_error;
488  has_tempfile = true;
489 
491 
492  /* check for multipart/related and save attachments with a Content-ID */
493  if (mutt_str_equal(type, "text/html"))
494  {
495  struct Body *related_ancestor = NULL;
496  if (actx->body_idx && (WithCrypto != 0) && (e->security & SEC_ENCRYPT))
497  related_ancestor = attach_body_ancestor(actx->body_idx[0], a, "related");
498  else
499  related_ancestor = attach_body_ancestor(e->body, a, "related");
500  if (related_ancestor)
501  {
502  struct CidMapList cid_map_list = STAILQ_HEAD_INITIALIZER(cid_map_list);
503  mutt_debug(LL_DEBUG2, "viewing text/html attachment in multipart/related group\n");
504  /* save attachments and build cid_map_list Content-ID to filename mapping list */
505  cid_save_attachments(related_ancestor->parts, &cid_map_list);
506  /* replace Content-IDs with filenames */
507  cid_to_filename(tmpfile, &cid_map_list);
508  /* empty Content-ID to filename mapping list */
509  cid_map_list_clear(&cid_map_list);
510  }
511  }
512 
513  use_pipe = mailcap_expand_command(a, mutt_buffer_string(tmpfile), type, cmd);
514  use_pager = entry->copiousoutput;
515  }
516 
517  if (use_pager)
518  {
519  if (fp && !use_mailcap && a->filename)
520  {
521  /* recv case */
522  mutt_buffer_strcpy(pagerfile, a->filename);
523  mutt_adv_mktemp(pagerfile);
524  }
525  else
526  mutt_buffer_mktemp(pagerfile);
527  }
528 
529  if (use_mailcap)
530  {
531  pid_t pid = 0;
532  int fd_temp = -1, fd_pager = -1;
533 
534  if (!use_pager)
535  mutt_endwin();
536 
537  const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
538  if (use_pager || use_pipe)
539  {
540  if (use_pager && ((fd_pager = mutt_file_open(mutt_buffer_string(pagerfile),
541  O_CREAT | O_EXCL | O_WRONLY)) == -1))
542  {
543  mutt_perror("open");
544  goto return_error;
545  }
546  unlink_pagerfile = true;
547 
548  if (use_pipe && ((fd_temp = open(mutt_buffer_string(tmpfile), 0)) == -1))
549  {
550  if (fd_pager != -1)
551  close(fd_pager);
552  mutt_perror("open");
553  goto return_error;
554  }
555  unlink_pagerfile = true;
556 
557  pid = filter_create_fd(mutt_buffer_string(cmd), NULL, NULL, NULL,
558  use_pipe ? fd_temp : -1, use_pager ? fd_pager : -1, -1);
559 
560  if (pid == -1)
561  {
562  if (fd_pager != -1)
563  close(fd_pager);
564 
565  if (fd_temp != -1)
566  close(fd_temp);
567 
568  mutt_error(_("Can't create filter"));
569  goto return_error;
570  }
571 
572  if (use_pager)
573  {
574  if (a->description)
575  {
576  snprintf(desc, sizeof(desc), _("---Command: %-20.20s Description: %s"),
578  }
579  else
580  {
581  snprintf(desc, sizeof(desc), _("---Command: %-30.30s Attachment: %s"),
582  mutt_buffer_string(cmd), type);
583  }
584  filter_wait(pid);
585  }
586  else
587  {
588  if (wait_interactive_filter(pid) || (entry->needsterminal && c_wait_key))
590  }
591 
592  if (fd_temp != -1)
593  close(fd_temp);
594  if (fd_pager != -1)
595  close(fd_pager);
596  }
597  else
598  {
599  /* interactive cmd */
600  int rv = mutt_system(mutt_buffer_string(cmd));
601  if (rv == -1)
602  mutt_debug(LL_DEBUG1, "Error running \"%s\"\n", cmd->data);
603 
604  if ((rv != 0) || (entry->needsterminal && c_wait_key))
606  }
607  }
608  else
609  {
610  /* Don't use mailcap; the attachment is viewed in the pager */
611 
612  if (mode == MUTT_VA_AS_TEXT)
613  {
614  /* just let me see the raw data */
615  if (fp)
616  {
617  /* Viewing from a received message.
618  *
619  * Don't use mutt_save_attachment() because we want to perform charset
620  * conversion since this will be displayed by the internal pager. */
621  struct State decode_state = { 0 };
622 
623  decode_state.fp_out = mutt_file_fopen(mutt_buffer_string(pagerfile), "w");
624  if (!decode_state.fp_out)
625  {
626  mutt_debug(LL_DEBUG1, "mutt_file_fopen(%s) errno=%d %s\n",
627  mutt_buffer_string(pagerfile), errno, strerror(errno));
628  mutt_perror(mutt_buffer_string(pagerfile));
629  goto return_error;
630  }
631  decode_state.fp_in = fp;
632  decode_state.flags = MUTT_CHARCONV;
633  mutt_decode_attachment(a, &decode_state);
634  if (mutt_file_fclose(&decode_state.fp_out) == EOF)
635  {
636  mutt_debug(LL_DEBUG1, "fclose(%s) errno=%d %s\n",
637  mutt_buffer_string(pagerfile), errno, strerror(errno));
638  }
639  }
640  else
641  {
642  /* in compose mode, just copy the file. we can't use
643  * mutt_decode_attachment() since it assumes the content-encoding has
644  * already been applied */
645  if (mutt_save_attachment(fp, a, mutt_buffer_string(pagerfile), MUTT_SAVE_NO_FLAGS, NULL))
646  goto return_error;
647  unlink_pagerfile = true;
648  }
650  }
651  else
652  {
653  /* Use built-in handler */
654  if (mutt_decode_save_attachment(fp, a, mutt_buffer_string(pagerfile),
656  {
657  goto return_error;
658  }
659  unlink_pagerfile = true;
660  }
661 
662  if (a->description)
663  mutt_str_copy(desc, a->description, sizeof(desc));
664  else if (a->filename)
665  snprintf(desc, sizeof(desc), _("---Attachment: %s: %s"), a->filename, type);
666  else
667  snprintf(desc, sizeof(desc), _("---Attachment: %s"), type);
668  }
669 
670  /* We only reach this point if there have been no errors */
671 
672  if (use_pager)
673  {
674  struct PagerData pdata = { 0 };
675  struct PagerView pview = { &pdata };
676 
677  pdata.actx = actx;
678  pdata.body = a;
679  pdata.fname = mutt_buffer_string(pagerfile);
680  pdata.fp = fp;
681 
682  pview.banner = desc;
683  pview.flags = MUTT_PAGER_ATTACHMENT |
684  (is_message ? MUTT_PAGER_MESSAGE : MUTT_PAGER_NO_FLAGS) |
685  ((use_mailcap && entry->xneomuttnowrap) ? MUTT_PAGER_NOWRAP :
687  pview.mode = PAGER_MODE_ATTACH;
688 
689  rc = mutt_do_pager(&pview, e);
690 
691  mutt_buffer_reset(pagerfile);
692  unlink_pagerfile = false;
693  }
694  else
695  rc = 0;
696 
697 return_error:
698 
699  if (!entry || !entry->xneomuttkeep)
700  {
701  if ((fp && !mutt_buffer_is_empty(tmpfile)) || has_tempfile)
702  {
703  /* add temporary file to TempAttachmentsList to be deleted on timeout hook */
705  }
706  }
707 
708  mailcap_entry_free(&entry);
709 
710  if (unlink_pagerfile)
712 
713  mutt_buffer_pool_release(&tmpfile);
714  mutt_buffer_pool_release(&pagerfile);
716  mutt_envlist_unset("COLUMNS");
717 
718  return rc;
719 }
720 
730 int mutt_pipe_attachment(FILE *fp, struct Body *b, const char *path, char *outfile)
731 {
732  pid_t pid = 0;
733  int out = -1, rc = 0;
734  bool is_flowed = false;
735  bool unlink_unstuff = false;
736  FILE *fp_filter = NULL, *fp_unstuff = NULL, *fp_in = NULL;
737  struct Buffer *unstuff_tempfile = NULL;
738 
739  if (outfile && *outfile)
740  {
741  out = mutt_file_open(outfile, O_CREAT | O_EXCL | O_WRONLY);
742  if (out < 0)
743  {
744  mutt_perror("open");
745  return 0;
746  }
747  }
748 
750  {
751  is_flowed = true;
752  unstuff_tempfile = mutt_buffer_pool_get();
753  mutt_buffer_mktemp(unstuff_tempfile);
754  }
755 
756  mutt_endwin();
757 
758  if (outfile && *outfile)
759  pid = filter_create_fd(path, &fp_filter, NULL, NULL, -1, out, -1);
760  else
761  pid = filter_create(path, &fp_filter, NULL, NULL);
762  if (pid < 0)
763  {
764  mutt_perror(_("Can't create filter"));
765  goto bail;
766  }
767 
768  /* recv case */
769  if (fp)
770  {
771  struct State s = { 0 };
772 
773  /* perform charset conversion on text attachments when piping */
774  s.flags = MUTT_CHARCONV;
775 
776  if (is_flowed)
777  {
778  fp_unstuff = mutt_file_fopen(mutt_buffer_string(unstuff_tempfile), "w");
779  if (fp_unstuff == NULL)
780  {
781  mutt_perror("mutt_file_fopen");
782  goto bail;
783  }
784  unlink_unstuff = true;
785 
786  s.fp_in = fp;
787  s.fp_out = fp_unstuff;
788  mutt_decode_attachment(b, &s);
789  mutt_file_fclose(&fp_unstuff);
790 
792 
793  fp_unstuff = mutt_file_fopen(mutt_buffer_string(unstuff_tempfile), "r");
794  if (fp_unstuff == NULL)
795  {
796  mutt_perror("mutt_file_fopen");
797  goto bail;
798  }
799  mutt_file_copy_stream(fp_unstuff, fp_filter);
800  mutt_file_fclose(&fp_unstuff);
801  }
802  else
803  {
804  s.fp_in = fp;
805  s.fp_out = fp_filter;
806  mutt_decode_attachment(b, &s);
807  }
808  }
809 
810  /* send case */
811  else
812  {
813  const char *infile = NULL;
814 
815  if (is_flowed)
816  {
817  if (mutt_save_attachment(fp, b, mutt_buffer_string(unstuff_tempfile),
818  MUTT_SAVE_NO_FLAGS, NULL) == -1)
819  {
820  goto bail;
821  }
822  unlink_unstuff = true;
824  infile = mutt_buffer_string(unstuff_tempfile);
825  }
826  else
827  infile = b->filename;
828 
829  fp_in = fopen(infile, "r");
830  if (!fp_in)
831  {
832  mutt_perror("fopen");
833  goto bail;
834  }
835 
836  mutt_file_copy_stream(fp_in, fp_filter);
838  }
839 
840  mutt_file_fclose(&fp_filter);
841  rc = 1;
842 
843 bail:
844  if (outfile && *outfile)
845  {
846  close(out);
847  if (rc == 0)
848  unlink(outfile);
849  else if (is_flowed)
851  }
852 
853  mutt_file_fclose(&fp_unstuff);
854  mutt_file_fclose(&fp_filter);
856 
857  if (unlink_unstuff)
858  mutt_file_unlink(mutt_buffer_string(unstuff_tempfile));
859  mutt_buffer_pool_release(&unstuff_tempfile);
860 
861  /* check for error exit from child process */
862  if ((pid > 0) && (filter_wait(pid) != 0))
863  rc = 0;
864 
865  const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
866  if ((rc == 0) || c_wait_key)
868  return rc;
869 }
870 
877 static FILE *save_attachment_open(const char *path, enum SaveAttach opt)
878 {
879  if (opt == MUTT_SAVE_APPEND)
880  return fopen(path, "a");
881  if (opt == MUTT_SAVE_OVERWRITE)
882  return fopen(path, "w");
883 
884  return mutt_file_fopen(path, "w");
885 }
886 
897 int mutt_save_attachment(FILE *fp, struct Body *m, const char *path,
898  enum SaveAttach opt, struct Email *e)
899 {
900  if (!m)
901  return -1;
902 
903  if (fp)
904  {
905  /* recv mode */
906 
907  if (e && m->email && (m->encoding != ENC_BASE64) &&
909  {
910  /* message type attachments are written to mail folders. */
911 
912  char buf[8192];
913  struct Message *msg = NULL;
914  CopyHeaderFlags chflags = CH_NO_FLAGS;
915  int rc = -1;
916 
917  struct Email *e_new = m->email;
918  e_new->msgno = e->msgno; /* required for MH/maildir */
919  e_new->read = true;
920 
921  if (!mutt_file_seek(fp, m->offset, SEEK_SET))
922  return -1;
923  if (!fgets(buf, sizeof(buf), fp))
924  return -1;
925  struct Mailbox *m_att = mx_path_resolve(path);
926  if (!mx_mbox_open(m_att, MUTT_APPEND | MUTT_QUIET))
927  {
928  mailbox_free(&m_att);
929  return -1;
930  }
931  msg = mx_msg_open_new(m_att, e_new,
932  is_from(buf, NULL, 0, NULL) ? MUTT_MSG_NO_FLAGS : MUTT_ADD_FROM);
933  if (!msg)
934  {
935  mx_mbox_close(m_att);
936  return -1;
937  }
938  if ((m_att->type == MUTT_MBOX) || (m_att->type == MUTT_MMDF))
939  chflags = CH_FROM | CH_UPDATE_LEN;
940  chflags |= ((m_att->type == MUTT_MAILDIR) ? CH_NOSTATUS : CH_UPDATE);
941  if ((mutt_copy_message_fp(msg->fp, fp, e_new, MUTT_CM_NO_FLAGS, chflags, 0) == 0) &&
942  (mx_msg_commit(m_att, msg) == 0))
943  {
944  rc = 0;
945  }
946  else
947  {
948  rc = -1;
949  }
950 
951  mx_msg_close(m_att, &msg);
952  mx_mbox_close(m_att);
953  return rc;
954  }
955  else
956  {
957  /* In recv mode, extract from folder and decode */
958 
959  struct State s = { 0 };
960 
961  s.fp_out = save_attachment_open(path, opt);
962  if (!s.fp_out)
963  {
964  mutt_perror("fopen");
965  return -1;
966  }
967  if (!mutt_file_seek((s.fp_in = fp), m->offset, SEEK_SET))
968  {
970  return -1;
971  }
972  mutt_decode_attachment(m, &s);
973 
974  if (mutt_file_fsync_close(&s.fp_out) != 0)
975  {
976  mutt_perror("fclose");
977  return -1;
978  }
979  }
980  }
981  else
982  {
983  if (!m->filename)
984  return -1;
985 
986  /* In send mode, just copy file */
987 
988  FILE *fp_old = fopen(m->filename, "r");
989  if (!fp_old)
990  {
991  mutt_perror("fopen");
992  return -1;
993  }
994 
995  FILE *fp_new = save_attachment_open(path, opt);
996  if (!fp_new)
997  {
998  mutt_perror("fopen");
999  mutt_file_fclose(&fp_old);
1000  return -1;
1001  }
1002 
1003  if (mutt_file_copy_stream(fp_old, fp_new) == -1)
1004  {
1005  mutt_error(_("Write fault"));
1006  mutt_file_fclose(&fp_old);
1007  mutt_file_fclose(&fp_new);
1008  return -1;
1009  }
1010  mutt_file_fclose(&fp_old);
1011  if (mutt_file_fsync_close(&fp_new) != 0)
1012  {
1013  mutt_error(_("Write fault"));
1014  return -1;
1015  }
1016  }
1017 
1018  return 0;
1019 }
1020 
1031 int mutt_decode_save_attachment(FILE *fp, struct Body *m, const char *path,
1032  int displaying, enum SaveAttach opt)
1033 {
1034  struct State s = { 0 };
1035  unsigned int saved_encoding = 0;
1036  struct Body *saved_parts = NULL;
1037  struct Email *e_saved = NULL;
1038  int rc = 0;
1039 
1040  s.flags = displaying;
1041 
1042  if (opt == MUTT_SAVE_APPEND)
1043  s.fp_out = fopen(path, "a");
1044  else if (opt == MUTT_SAVE_OVERWRITE)
1045  s.fp_out = fopen(path, "w");
1046  else
1047  s.fp_out = mutt_file_fopen(path, "w");
1048 
1049  if (!s.fp_out)
1050  {
1051  mutt_perror("fopen");
1052  return -1;
1053  }
1054 
1055  if (!fp)
1056  {
1057  /* When called from the compose menu, the attachment isn't parsed,
1058  * so we need to do it here. */
1059  s.fp_in = fopen(m->filename, "r");
1060  if (!s.fp_in)
1061  {
1062  mutt_perror("fopen");
1064  return -1;
1065  }
1066 
1067  struct stat st = { 0 };
1068  if (fstat(fileno(s.fp_in), &st) == -1)
1069  {
1070  mutt_perror("stat");
1071  mutt_file_fclose(&s.fp_in);
1073  return -1;
1074  }
1075 
1076  saved_encoding = m->encoding;
1077  if (!is_multipart(m))
1078  m->encoding = ENC_8BIT;
1079 
1080  m->length = st.st_size;
1081  m->offset = 0;
1082  saved_parts = m->parts;
1083  e_saved = m->email;
1084  mutt_parse_part(s.fp_in, m);
1085 
1086  if (m->noconv || is_multipart(m))
1087  s.flags |= MUTT_CHARCONV;
1088  }
1089  else
1090  {
1091  s.fp_in = fp;
1092  s.flags |= MUTT_CHARCONV;
1093  }
1094 
1095  mutt_body_handler(m, &s);
1096 
1097  if (mutt_file_fsync_close(&s.fp_out) != 0)
1098  {
1099  mutt_perror("fclose");
1100  rc = -1;
1101  }
1102  if (!fp)
1103  {
1104  m->length = 0;
1105  m->encoding = saved_encoding;
1106  if (saved_parts)
1107  {
1108  email_free(&m->email);
1109  m->parts = saved_parts;
1110  m->email = e_saved;
1111  }
1112  mutt_file_fclose(&s.fp_in);
1113  }
1114 
1115  return rc;
1116 }
1117 
1131 int mutt_print_attachment(FILE *fp, struct Body *a)
1132 {
1133  char type[256];
1134  pid_t pid;
1135  FILE *fp_in = NULL, *fp_out = NULL;
1136  bool unlink_newfile = false;
1137  struct Buffer *newfile = mutt_buffer_pool_get();
1138  struct Buffer *cmd = mutt_buffer_pool_get();
1139 
1140  int rc = 0;
1141 
1142  snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
1143 
1144  if (mailcap_lookup(a, type, sizeof(type), NULL, MUTT_MC_PRINT))
1145  {
1146  mutt_debug(LL_DEBUG2, "Using mailcap\n");
1147 
1148  struct MailcapEntry *entry = mailcap_entry_new();
1149  mailcap_lookup(a, type, sizeof(type), entry, MUTT_MC_PRINT);
1150 
1151  char *sanitized_fname = mutt_str_dup(a->filename);
1152  /* In send mode (!fp), we allow slashes because those are part of
1153  * the tempfile. The path will be removed in expand_filename */
1154  mutt_file_sanitize_filename(sanitized_fname, fp ? true : false);
1155  mailcap_expand_filename(entry->nametemplate, sanitized_fname, newfile);
1156  FREE(&sanitized_fname);
1157 
1158  if (mutt_save_attachment(fp, a, mutt_buffer_string(newfile),
1159  MUTT_SAVE_NO_FLAGS, NULL) == -1)
1160  {
1161  goto mailcap_cleanup;
1162  }
1163  unlink_newfile = 1;
1164 
1166 
1167  mutt_buffer_strcpy(cmd, entry->printcommand);
1168 
1169  bool piped = mailcap_expand_command(a, mutt_buffer_string(newfile), type, cmd);
1170 
1171  mutt_endwin();
1172 
1173  const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
1174  /* interactive program */
1175  if (piped)
1176  {
1177  fp_in = fopen(mutt_buffer_string(newfile), "r");
1178  if (!fp_in)
1179  {
1180  mutt_perror("fopen");
1181  mailcap_entry_free(&entry);
1182  goto mailcap_cleanup;
1183  }
1184 
1185  pid = filter_create(mutt_buffer_string(cmd), &fp_out, NULL, NULL);
1186  if (pid < 0)
1187  {
1188  mutt_perror(_("Can't create filter"));
1189  mailcap_entry_free(&entry);
1190  mutt_file_fclose(&fp_in);
1191  goto mailcap_cleanup;
1192  }
1193  mutt_file_copy_stream(fp_in, fp_out);
1194  mutt_file_fclose(&fp_out);
1195  mutt_file_fclose(&fp_in);
1196  if (filter_wait(pid) || c_wait_key)
1198  }
1199  else
1200  {
1201  int rc2 = mutt_system(mutt_buffer_string(cmd));
1202  if (rc2 == -1)
1203  mutt_debug(LL_DEBUG1, "Error running \"%s\"\n", cmd->data);
1204 
1205  if ((rc2 != 0) || c_wait_key)
1207  }
1208 
1209  rc = 1;
1210 
1211  mailcap_cleanup:
1212  if (unlink_newfile)
1214 
1215  mailcap_entry_free(&entry);
1216  goto out;
1217  }
1218 
1219  const char *const c_print_command = cs_subset_string(NeoMutt->sub, "print_command");
1220  if (mutt_istr_equal("text/plain", type) || mutt_istr_equal("application/postscript", type))
1221  {
1222  rc = (mutt_pipe_attachment(fp, a, NONULL(c_print_command), NULL));
1223  goto out;
1224  }
1225  else if (mutt_can_decode(a))
1226  {
1227  /* decode and print */
1228 
1229  fp_in = NULL;
1230  fp_out = NULL;
1231 
1232  mutt_buffer_mktemp(newfile);
1233  if (mutt_decode_save_attachment(fp, a, mutt_buffer_string(newfile),
1235  {
1236  unlink_newfile = true;
1237  mutt_debug(LL_DEBUG2, "successfully decoded %s type attachment to %s\n",
1238  type, mutt_buffer_string(newfile));
1239 
1240  fp_in = fopen(mutt_buffer_string(newfile), "r");
1241  if (!fp_in)
1242  {
1243  mutt_perror("fopen");
1244  goto decode_cleanup;
1245  }
1246 
1247  mutt_debug(LL_DEBUG2, "successfully opened %s read-only\n",
1248  mutt_buffer_string(newfile));
1249 
1250  mutt_endwin();
1251  pid = filter_create(NONULL(c_print_command), &fp_out, NULL, NULL);
1252  if (pid < 0)
1253  {
1254  mutt_perror(_("Can't create filter"));
1255  goto decode_cleanup;
1256  }
1257 
1258  mutt_debug(LL_DEBUG2, "Filter created\n");
1259 
1260  mutt_file_copy_stream(fp_in, fp_out);
1261 
1262  mutt_file_fclose(&fp_out);
1263  mutt_file_fclose(&fp_in);
1264 
1265  const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
1266  if ((filter_wait(pid) != 0) || c_wait_key)
1268  rc = 1;
1269  }
1270  decode_cleanup:
1271  mutt_file_fclose(&fp_in);
1272  mutt_file_fclose(&fp_out);
1273  if (unlink_newfile)
1275  }
1276  else
1277  {
1278  mutt_error(_("I don't know how to print that"));
1279  rc = 0;
1280  }
1281 
1282 out:
1283  mutt_buffer_pool_release(&newfile);
1285 
1286  return rc;
1287 }
1288 
1293 void mutt_add_temp_attachment(const char *filename)
1294 {
1296 }
1297 
1302 {
1303  struct ListNode *np = NULL;
1304 
1305  STAILQ_FOREACH(np, &TempAttachmentsList, entries)
1306  {
1307  (void) mutt_file_chmod_add(np->data, S_IWUSR);
1308  mutt_file_unlink(np->data);
1309  }
1310 
1312 }
struct Body * attach_body_ancestor(struct Body *start, struct Body *body, const char *subtype)
Find the ancestor of a body with specified subtype.
Definition: lib.c:116
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:250
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:310
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:81
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
void cid_save_attachments(struct Body *body, struct CidMapList *cid_map_list)
Save all attachments in a "multipart/related" group with a Content-ID.
Definition: cid.c:150
void cid_to_filename(struct Buffer *filename, const struct CidMapList *cid_map_list)
Replace Content-IDs with filenames.
Definition: cid.c:169
void cid_map_list_clear(struct CidMapList *cid_map_list)
Empty a CidMapList.
Definition: cid.c:81
Attachment Content-ID header functions.
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
Convenience wrapper for the config headers.
int mutt_copy_message_fp(FILE *fp_out, FILE *fp_in, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
Make a copy of a message from a FILE pointer.
Definition: copy.c:640
Duplicate the structure of an entire email.
#define CH_UPDATE
Update the status and x-status fields?
Definition: copy.h:52
#define CH_NOSTATUS
Suppress the status and x-status fields.
Definition: copy.h:58
#define CH_FROM
Retain the "From " message separator?
Definition: copy.h:56
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition: copy.h:50
#define CH_UPDATE_LEN
Update Lines: and Content-Length:
Definition: copy.h:62
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition: copy.h:35
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:51
Convenience wrapper for the core headers.
bool crypt_valid_passphrase(SecurityFlags flags)
Check that we have a usable passphrase, ask if not.
Definition: crypt.c:134
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition: curs_lib.c:313
int mutt_any_key_to_continue(const char *s)
Prompt the user to 'press any key' and wait.
Definition: curs_lib.c:388
void mutt_endwin(void)
Shutdown curses.
Definition: curs_lib.c:355
int mutt_do_pager(struct PagerView *pview, struct Email *e)
Display some page-able text to the user (help or attachment)
Definition: do_pager.c:120
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:44
Structs that make up an email.
bool mutt_envlist_unset(const char *name)
Unset an environment variable.
Definition: envlist.c:132
bool mutt_envlist_set(const char *name, const char *value, bool overwrite)
Set an environment variable.
Definition: envlist.c:85
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:260
int mutt_file_open(const char *path, uint32_t flags)
Open a file.
Definition: file.c:549
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:647
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:690
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition: file.c:1403
int mutt_file_symlink(const char *oldpath, const char *newpath)
Create a symlink.
Definition: file.c:288
int mutt_file_chmod_add(const char *path, mode_t mode)
Add permissions to a file.
Definition: file.c:1123
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:618
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:194
int mutt_file_fsync_close(FILE **fp)
Flush the data, before closing a file (and NULL the pointer)
Definition: file.c:168
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:206
pid_t filter_create_fd(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err, int fdin, int fdout, int fderr)
Run a command on a pipe (optionally connect stdin/stdout)
Definition: filter.c:61
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition: from.c:48
#define mutt_error(...)
Definition: logging.h:87
#define mutt_message(...)
Definition: logging.h:86
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
#define mutt_perror(...)
Definition: logging.h:88
Convenience wrapper for the gui headers.
bool mutt_can_decode(struct Body *b)
Will decoding the attachment produce any output.
Definition: handler.c:1827
void mutt_decode_attachment(struct Body *b, struct State *s)
Decode an email's attachment.
Definition: handler.c:1867
int mutt_body_handler(struct Body *b, struct State *s)
Handler for the Body of an email.
Definition: handler.c:1597
Decide how to display email content.
IMAP network mailbox.
int imap_wait_keepalive(pid_t pid)
Wait for a process to change state.
Definition: util.c:969
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:87
@ MUTT_MMDF
'mmdf' Mailbox type
Definition: mailbox.h:46
@ MUTT_MBOX
'mbox' Mailbox type
Definition: mailbox.h:45
@ MUTT_MAILDIR
'Maildir' Mailbox type
Definition: mailbox.h:48
struct MailcapEntry * mailcap_entry_new(void)
Allocate memory for a new rfc1524 entry.
Definition: mailcap.c:435
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:473
void mailcap_entry_free(struct MailcapEntry **ptr)
Deallocate an struct MailcapEntry.
Definition: mailcap.c:444
int mailcap_expand_command(struct Body *a, const char *filename, const char *type, struct Buffer *command)
Expand expandos in a command.
Definition: mailcap.c:67
void mailcap_expand_filename(const char *nametemplate, const char *oldfile, struct Buffer *newfile)
Expand a new filename from a template or existing filename.
Definition: mailcap.c:542
RFC1524 Mailcap routines.
MailcapLookup
Mailcap actions.
Definition: mailcap.h:55
@ MUTT_MC_PRINT
Mailcap print field.
Definition: mailcap.h:59
@ MUTT_MC_EDIT
Mailcap edit field.
Definition: mailcap.h:57
@ MUTT_MC_AUTOVIEW
Mailcap autoview field.
Definition: mailcap.h:60
@ MUTT_MC_NO_FLAGS
No flags set.
Definition: mailcap.h:56
@ MUTT_MC_COMPOSE
Mailcap compose field.
Definition: mailcap.h:58
#define FREE(x)
Definition: memory.h:43
@ ENC_BASE64
Base-64 encoded text.
Definition: mime.h:52
@ ENC_8BIT
8-bit text
Definition: mime.h:50
@ ENC_QUOTED_PRINTABLE
Quoted-printable text.
Definition: mime.h:51
ContentType
Content-Type.
Definition: mime.h:30
@ TYPE_AUDIO
Type: 'audio/*'.
Definition: mime.h:32
@ TYPE_IMAGE
Type: 'image/*'.
Definition: mime.h:34
@ TYPE_OTHER
Unknown Content-Type.
Definition: mime.h:31
@ TYPE_MESSAGE
Type: 'message/*'.
Definition: mime.h:35
@ TYPE_MODEL
Type: 'model/*'.
Definition: mime.h:36
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition: mime.h:37
@ TYPE_APPLICATION
Type: 'application/*'.
Definition: mime.h:33
@ TYPE_TEXT
Type: 'text/*'.
Definition: mime.h:38
@ TYPE_VIDEO
Type: 'video/*'.
Definition: mime.h:39
#define TYPE(body)
Definition: mime.h:89
#define is_multipart(body)
Definition: mime.h:82
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
#define MUTT_CHARCONV
Do character set conversions.
Definition: state.h:36
#define MUTT_DISPLAY
Output is displayed to the user.
Definition: state.h:32
#define MUTT_DISPLAY_ATTACH
We are displaying an attachment.
Definition: state.h:40
#define MUTT_PRINTING
Are we printing? - MUTT_DISPLAY "light".
Definition: state.h:37
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:796
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:784
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:544
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:629
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:326
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition: string.c:501
int mutt_pipe_attachment(FILE *fp, struct Body *b, const char *path, char *outfile)
Pipe an attachment to a command.
Definition: mutt_attach.c:730
void mutt_check_lookup_list(struct Body *b, char *type, size_t len)
Update the mime type.
Definition: mutt_attach.c:337
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:1031
int mutt_print_attachment(FILE *fp, struct Body *a)
Print out an attachment.
Definition: mutt_attach.c:1131
static int wait_interactive_filter(pid_t pid)
Wait after an interactive filter.
Definition: mutt_attach.c:384
static FILE * save_attachment_open(const char *path, enum SaveAttach opt)
Open a file to write an attachment to.
Definition: mutt_attach.c:877
int mutt_get_tmp_attachment(struct Body *a)
Get a temporary copy of an attachment.
Definition: mutt_attach.c:69
void mutt_add_temp_attachment(const char *filename)
Add file to list of temporary attachments.
Definition: mutt_attach.c:1293
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:417
int mutt_compose_attachment(struct Body *a)
Create an attachment.
Definition: mutt_attach.c:115
void mutt_unlink_temp_attachments(void)
Delete all temporary attachments.
Definition: mutt_attach.c:1301
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:897
int mutt_edit_attachment(struct Body *a)
Edit an attachment.
Definition: mutt_attach.c:260
Handling of email attachments.
SaveAttach
Options for saving attachments.
Definition: mutt_attach.h:56
@ MUTT_SAVE_APPEND
Append to existing file.
Definition: mutt_attach.h:58
@ MUTT_SAVE_OVERWRITE
Overwrite existing file.
Definition: mutt_attach.h:59
@ MUTT_SAVE_NO_FLAGS
No flags set.
Definition: mutt_attach.h:57
ViewAttachMode
Options for mutt_view_attachment()
Definition: mutt_attach.h:42
@ MUTT_VA_MAILCAP
Force viewing using mailcap entry.
Definition: mutt_attach.h:44
@ MUTT_VA_REGULAR
View using default method.
Definition: mutt_attach.h:43
@ MUTT_VA_PAGER
View attachment in pager using copiousoutput mailcap.
Definition: mutt_attach.h:46
@ MUTT_VA_AS_TEXT
Force viewing as text.
Definition: mutt_attach.h:45
Hundreds of global variables to back the user variables.
struct ListHead TempAttachmentsList
List of temporary files for displaying attachments.
Definition: mutt_globals.h:65
struct ListHead MimeLookupList
List of mime types that that shouldn't use the mailcap entry.
Definition: mutt_globals.h:63
bool mutt_needs_mailcap(struct Body *m)
Does this type need a mailcap entry do display.
Definition: muttlib.c:405
void mutt_adv_mktemp(struct Buffer *buf)
Create a temporary file.
Definition: muttlib.c:84
Some miscellaneous functions.
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1193
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:304
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1677
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition: mx.c:1172
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1057
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition: mx.c:615
API for mailboxes.
#define MUTT_ADD_FROM
add a From_ line
Definition: mx.h:43
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition: mx.h:42
#define MUTT_APPEND
Open mailbox for appending messages.
Definition: mxapi.h:63
#define MUTT_QUIET
Do not print any messages.
Definition: mxapi.h:65
API for encryption/signing of emails.
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:78
#define WithCrypto
Definition: lib.h:116
GUI display a file/email/help in a viewport with paging.
#define MUTT_PAGER_NO_FLAGS
No flags are set.
Definition: lib.h:58
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition: lib.h:71
@ PAGER_MODE_ATTACH
Pager is invoked via 2nd path. A user-selected attachment (mime part or a nested email) will be shown...
Definition: lib.h:136
#define MUTT_PAGER_MESSAGE
Definition: lib.h:74
#define MUTT_PAGER_ATTACHMENT
Attachments may exist.
Definition: lib.h:70
void mutt_param_free(struct ParameterList *pl)
Free a ParameterList.
Definition: parameter.c:61
void mutt_parse_part(FILE *fp, struct Body *b)
Parse a MIME part.
Definition: parse.c:1737
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition: parse.c:1318
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1442
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
Prototypes for many functions.
int mutt_system(const char *cmd)
Run an external command.
Definition: system.c:51
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:39
Ask the user a question.
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:194
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
#define TAILQ_INIT(head)
Definition: queue.h:765
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define TAILQ_EMPTY(head)
Definition: queue.h:721
void mutt_rfc3676_space_stuff_attachment(struct Body *b, const char *filename)
Stuff attachments.
Definition: rfc3676.c:534
void mutt_rfc3676_space_unstuff_attachment(struct Body *b, const char *filename)
Unstuff attachments.
Definition: rfc3676.c:513
bool mutt_rfc3676_is_format_flowed(struct Body *b)
Is the Email "format-flowed"?
Definition: rfc3676.c:389
RFC3676 Format Flowed routines.
Convenience wrapper for the send headers.
void mutt_stamp_attachment(struct Body *a)
Timestamp an Attachment.
Definition: sendlib.c:401
enum ContentType mutt_lookup_mime_type(struct Body *att, const char *path)
Find the MIME type for an attachment.
Definition: sendlib.c:71
void mutt_sig_unblock_system(bool restore)
Restore previously blocked signals.
Definition: signal.c:207
Key value store.
#define NONULL(x)
Definition: string2.h:37
A set of attachments.
Definition: attach.h:51
struct Body ** body_idx
Extra struct Body* used for decryption.
Definition: attach.h:66
The body of an email.
Definition: body.h:36
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:72
LOFF_T offset
offset where the actual data begins
Definition: body.h:52
char * xtype
content-type if x-unknown
Definition: body.h:61
bool noconv
Don't do character set conversion.
Definition: body.h:46
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition: body.h:67
time_t stamp
Time stamp of last encoding update.
Definition: body.h:76
LOFF_T length
length (in bytes) of attachment
Definition: body.h:53
struct ParameterList parameter
Parameters of the content-type.
Definition: body.h:62
struct Email * email
header information for message/rfc822
Definition: body.h:73
char * description
content-description
Definition: body.h:55
char * subtype
content-type subtype
Definition: body.h:60
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:41
char * form_name
Content-Disposition form-data name param.
Definition: body.h:59
unsigned int type
content-type primary type, ContentType
Definition: body.h:40
char * filename
When sending a message, this is the file to which this structure refers.
Definition: body.h:58
String manipulation buffer.
Definition: buffer.h:34
char * data
Pointer to data.
Definition: buffer.h:35
The envelope/body of an email.
Definition: email.h:37
bool read
Email is read.
Definition: email.h:48
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib....
Definition: email.h:41
struct Body * body
List of MIME parts.
Definition: email.h:67
int msgno
Number displayed to the user.
Definition: email.h:111
char * path
Path of Email (for local Mailboxes)
Definition: email.h:68
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
A mailbox.
Definition: mailbox.h:79
enum MailboxType type
Mailbox type.
Definition: mailbox.h:102
A mailcap entry.
Definition: mailcap.h:36
char * composecommand
Definition: mailcap.h:39
bool needsterminal
endwin() and system
Definition: mailcap.h:45
char * nametemplate
Definition: mailcap.h:43
char * printcommand
Definition: mailcap.h:42
char * composetypecommand
Definition: mailcap.h:40
char * editcommand
Definition: mailcap.h:41
char * command
Definition: mailcap.h:37
bool copiousoutput
needs pager, basically
Definition: mailcap.h:46
bool xneomuttkeep
do not remove the file on command exit
Definition: mailcap.h:47
bool xneomuttnowrap
do not wrap the output in the pager
Definition: mailcap.h:48
A local copy of an email.
Definition: mxapi.h:43
FILE * fp
pointer to the message data
Definition: mxapi.h:44
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
Data to be displayed by PagerView.
Definition: lib.h:158
const char * fname
Name of the file to read.
Definition: lib.h:162
FILE * fp
Source stream.
Definition: lib.h:160
struct Body * body
Current attachment.
Definition: lib.h:159
struct AttachCtx * actx
Attachment information.
Definition: lib.h:161
Paged view into some data.
Definition: lib.h:169
struct PagerData * pdata
Data that pager displays. NOTNULL.
Definition: lib.h:170
enum PagerMode mode
Pager mode.
Definition: lib.h:171
PagerFlags flags
Additional settings to tweak pager's function.
Definition: lib.h:172
const char * banner
Title to display in status bar.
Definition: lib.h:173
Keep track when processing files.
Definition: state.h:46
StateFlags flags
Flags, e.g. MUTT_DISPLAY.
Definition: state.h:50
FILE * fp_out
File to write to.
Definition: state.h:48
FILE * fp_in
File to read from.
Definition: state.h:47
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:60