NeoMutt  2023-03-22-27-g3cb248
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 "globals.h" // IWYU pragma: keep
53#include "handler.h"
54#include "mailcap.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] = { 0 };
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);
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 {
100 mutt_perror(fp_in ? mutt_buffer_string(tmpfile) : a->filename);
101 }
102
103 mutt_file_fclose(&fp_in);
104 mutt_file_fclose(&fp_out);
105
106 mutt_buffer_pool_release(&tmpfile);
107
108 return a->unlink ? 0 : -1;
109}
110
118{
119 char type[256] = { 0 };
120 struct MailcapEntry *entry = mailcap_entry_new();
121 bool unlink_newfile = false;
122 int rc = 0;
123 struct Buffer *cmd = mutt_buffer_pool_get();
124 struct Buffer *newfile = mutt_buffer_pool_get();
125 struct Buffer *tmpfile = mutt_buffer_pool_get();
126
127 snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
128 if (mailcap_lookup(a, type, sizeof(type), entry, MUTT_MC_COMPOSE))
129 {
130 if (entry->composecommand || entry->composetypecommand)
131 {
132 if (entry->composetypecommand)
134 else
136
137 mailcap_expand_filename(entry->nametemplate, a->filename, newfile);
138 mutt_debug(LL_DEBUG1, "oldfile: %s newfile: %s\n", a->filename,
139 mutt_buffer_string(newfile));
140 if (mutt_file_symlink(a->filename, mutt_buffer_string(newfile)) == -1)
141 {
142 if (mutt_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
143 goto bailout;
144 mutt_buffer_strcpy(newfile, a->filename);
145 }
146 else
147 {
148 unlink_newfile = true;
149 }
150
151 if (mailcap_expand_command(a, mutt_buffer_string(newfile), type, cmd))
152 {
153 /* For now, editing requires a file, no piping */
154 mutt_error(_("Mailcap compose entry requires %%s"));
155 }
156 else
157 {
158 int r;
159
160 mutt_endwin();
162 if (r == -1)
163 mutt_error(_("Error running \"%s\""), mutt_buffer_string(cmd));
164
165 if ((r != -1) && entry->composetypecommand)
166 {
167 struct Body *b = NULL;
168
169 FILE *fp = mutt_file_fopen(a->filename, "r");
170 if (!fp)
171 {
172 mutt_perror(_("Failure to open file to parse headers"));
173 goto bailout;
174 }
175
176 b = mutt_read_mime_header(fp, 0);
177 if (b)
178 {
179 if (!TAILQ_EMPTY(&b->parameter))
180 {
182 a->parameter = b->parameter;
184 }
185 if (b->description)
186 {
187 FREE(&a->description);
188 a->description = b->description;
189 b->description = NULL;
190 }
191 if (b->form_name)
192 {
193 FREE(&a->form_name);
194 a->form_name = b->form_name;
195 b->form_name = NULL;
196 }
197
198 /* Remove headers by copying out data to another file, then
199 * copying the file back */
200 const LOFF_T offset = b->offset;
201 mutt_body_free(&b);
202 if (!mutt_file_seek(fp, offset, SEEK_SET))
203 {
204 goto bailout;
205 }
206
207 mutt_buffer_mktemp(tmpfile);
208 FILE *fp_tmp = mutt_file_fopen(mutt_buffer_string(tmpfile), "w");
209 if (!fp_tmp)
210 {
211 mutt_perror(_("Failure to open file to strip headers"));
212 mutt_file_fclose(&fp);
213 goto bailout;
214 }
215 mutt_file_copy_stream(fp, fp_tmp);
216 mutt_file_fclose(&fp);
217 mutt_file_fclose(&fp_tmp);
219 if (mutt_file_rename(mutt_buffer_string(tmpfile), a->filename) != 0)
220 {
221 mutt_perror(_("Failure to rename file"));
222 goto bailout;
223 }
224 }
225 }
226 }
227 }
228 }
229 else
230 {
231 mutt_message(_("No mailcap compose entry for %s, creating empty file"), type);
232 rc = 1;
233 goto bailout;
234 }
235
236 rc = 1;
237
238bailout:
239
240 if (unlink_newfile)
241 unlink(mutt_buffer_string(newfile));
242
244 mutt_buffer_pool_release(&newfile);
245 mutt_buffer_pool_release(&tmpfile);
246
247 mailcap_entry_free(&entry);
248 return rc;
249}
250
265{
266 char type[256] = { 0 };
267 struct MailcapEntry *entry = mailcap_entry_new();
268 bool unlink_newfile = false;
269 bool rc = false;
270 struct Buffer *cmd = mutt_buffer_pool_get();
271 struct Buffer *newfile = mutt_buffer_pool_get();
272
273 snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
274 if (mailcap_lookup(a, type, sizeof(type), entry, MUTT_MC_EDIT))
275 {
276 if (entry->editcommand)
277 {
278 mutt_buffer_strcpy(cmd, entry->editcommand);
279 mailcap_expand_filename(entry->nametemplate, a->filename, newfile);
280 mutt_debug(LL_DEBUG1, "oldfile: %s newfile: %s\n", a->filename,
281 mutt_buffer_string(newfile));
282 if (mutt_file_symlink(a->filename, mutt_buffer_string(newfile)) == -1)
283 {
284 if (mutt_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
285 goto bailout;
286 mutt_buffer_strcpy(newfile, a->filename);
287 }
288 else
289 {
290 unlink_newfile = true;
291 }
292
293 if (mailcap_expand_command(a, mutt_buffer_string(newfile), type, cmd))
294 {
295 /* For now, editing requires a file, no piping */
296 mutt_error(_("Mailcap Edit entry requires %%s"));
297 goto bailout;
298 }
299 else
300 {
301 mutt_endwin();
302 if (mutt_system(mutt_buffer_string(cmd)) == -1)
303 {
304 mutt_error(_("Error running \"%s\""), mutt_buffer_string(cmd));
305 goto bailout;
306 }
307 }
308 }
309 }
310 else if (a->type == TYPE_TEXT)
311 {
312 /* On text, default to editor */
313 const char *const c_editor = cs_subset_string(NeoMutt->sub, "editor");
314 mutt_edit_file(NONULL(c_editor), a->filename);
315 }
316 else
317 {
318 mutt_error(_("No mailcap edit entry for %s"), type);
319 goto bailout;
320 }
321
322 rc = true;
323
324bailout:
325
326 if (unlink_newfile)
327 unlink(mutt_buffer_string(newfile));
328
330 mutt_buffer_pool_release(&newfile);
331
332 mailcap_entry_free(&entry);
333 return rc;
334}
335
342void mutt_check_lookup_list(struct Body *b, char *type, size_t len)
343{
344 struct ListNode *np = NULL;
345 STAILQ_FOREACH(np, &MimeLookupList, entries)
346 {
347 const int i = mutt_str_len(np->data) - 1;
348 if (((i > 0) && (np->data[i - 1] == '/') && (np->data[i] == '*') &&
349 mutt_istrn_equal(type, np->data, i)) ||
350 mutt_istr_equal(type, np->data))
351 {
352 struct Body tmp = { 0 };
353 enum ContentType n;
354 if ((n = mutt_lookup_mime_type(&tmp, b->filename)) != TYPE_OTHER ||
356 {
357 snprintf(type, len, "%s/%s",
358 (n == TYPE_AUDIO) ? "audio" :
359 (n == TYPE_APPLICATION) ? "application" :
360 (n == TYPE_IMAGE) ? "image" :
361 (n == TYPE_MESSAGE) ? "message" :
362 (n == TYPE_MODEL) ? "model" :
363 (n == TYPE_MULTIPART) ? "multipart" :
364 (n == TYPE_TEXT) ? "text" :
365 (n == TYPE_VIDEO) ? "video" :
366 "other",
367 tmp.subtype);
368 mutt_debug(LL_DEBUG1, "\"%s\" -> %s\n", b->filename, type);
369 }
370 FREE(&tmp.subtype);
371 FREE(&tmp.xtype);
372 }
373 }
374}
375
389static int wait_interactive_filter(pid_t pid)
390{
391 int rc;
392
393#ifdef USE_IMAP
394 rc = imap_wait_keepalive(pid);
395#else
396 waitpid(pid, &rc, 0);
397#endif
399 rc = WIFEXITED(rc) ? WEXITSTATUS(rc) : -1;
400
401 return rc;
402}
403
422int mutt_view_attachment(FILE *fp, struct Body *a, enum ViewAttachMode mode,
423 struct Email *e, struct AttachCtx *actx, struct MuttWindow *win)
424{
425 bool use_mailcap = false;
426 bool use_pipe = false;
427 bool use_pager = true;
428 char type[256] = { 0 };
429 char desc[256] = { 0 };
430 char *fname = NULL;
431 struct MailcapEntry *entry = NULL;
432 int rc = -1;
433 bool has_tempfile = false;
434 bool unlink_pagerfile = false;
435
436 bool is_message = mutt_is_message_type(a->type, a->subtype);
437 if ((WithCrypto != 0) && is_message && a->email &&
439 {
440 return rc;
441 }
442
443 struct Buffer *tmpfile = mutt_buffer_pool_get();
444 struct Buffer *pagerfile = mutt_buffer_pool_get();
445 struct Buffer *cmd = mutt_buffer_pool_get();
446
447 use_mailcap = ((mode == MUTT_VA_MAILCAP) ||
448 ((mode == MUTT_VA_REGULAR) && mutt_needs_mailcap(a)) ||
449 (mode == MUTT_VA_PAGER));
450 snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
451
452 char columns[16] = { 0 };
453 snprintf(columns, sizeof(columns), "%d", win->state.cols);
454 mutt_envlist_set("COLUMNS", columns, true);
455
456 if (use_mailcap)
457 {
458 entry = mailcap_entry_new();
459 enum MailcapLookup mailcap_opt = (mode == MUTT_VA_PAGER) ? MUTT_MC_AUTOVIEW : MUTT_MC_NO_FLAGS;
460 if (!mailcap_lookup(a, type, sizeof(type), entry, mailcap_opt))
461 {
462 if ((mode == MUTT_VA_REGULAR) || (mode == MUTT_VA_PAGER))
463 {
464 /* fallback to view as text */
465 mailcap_entry_free(&entry);
466 mutt_error(_("No matching mailcap entry found. Viewing as text."));
467 mode = MUTT_VA_AS_TEXT;
468 use_mailcap = false;
469 }
470 else
471 {
472 goto return_error;
473 }
474 }
475 }
476
477 if (use_mailcap)
478 {
479 if (!entry->command)
480 {
481 mutt_error(_("MIME type not defined. Can't view attachment."));
482 goto return_error;
483 }
484 mutt_buffer_strcpy(cmd, entry->command);
485
486 fname = mutt_str_dup(a->filename);
487 /* In send mode(!fp), we allow slashes because those are part of
488 * the tmpfile. The path will be removed in expand_filename */
489 mutt_file_sanitize_filename(fname, fp ? true : false);
490 mailcap_expand_filename(entry->nametemplate, fname, tmpfile);
491 FREE(&fname);
492
493 if (mutt_save_attachment(fp, a, mutt_buffer_string(tmpfile), 0, NULL) == -1)
494 goto return_error;
495 has_tempfile = true;
496
498
499 /* check for multipart/related and save attachments with a Content-ID */
500 if (mutt_str_equal(type, "text/html"))
501 {
502 struct Body *related_ancestor = NULL;
503 if (actx->body_idx && (WithCrypto != 0) && (e->security & SEC_ENCRYPT))
504 related_ancestor = attach_body_ancestor(actx->body_idx[0], a, "related");
505 else
506 related_ancestor = attach_body_ancestor(e->body, a, "related");
507 if (related_ancestor)
508 {
509 struct CidMapList cid_map_list = STAILQ_HEAD_INITIALIZER(cid_map_list);
510 mutt_debug(LL_DEBUG2, "viewing text/html attachment in multipart/related group\n");
511 /* save attachments and build cid_map_list Content-ID to filename mapping list */
512 cid_save_attachments(related_ancestor->parts, &cid_map_list);
513 /* replace Content-IDs with filenames */
514 cid_to_filename(tmpfile, &cid_map_list);
515 /* empty Content-ID to filename mapping list */
516 cid_map_list_clear(&cid_map_list);
517 }
518 }
519
520 use_pipe = mailcap_expand_command(a, mutt_buffer_string(tmpfile), type, cmd);
521 use_pager = entry->copiousoutput;
522 }
523
524 if (use_pager)
525 {
526 if (fp && !use_mailcap && a->filename)
527 {
528 /* recv case */
529 mutt_buffer_strcpy(pagerfile, a->filename);
530 mutt_adv_mktemp(pagerfile);
531 }
532 else
533 {
534 mutt_buffer_mktemp(pagerfile);
535 }
536 }
537
538 if (use_mailcap)
539 {
540 pid_t pid = 0;
541 int fd_temp = -1, fd_pager = -1;
542
543 if (!use_pager)
544 mutt_endwin();
545
546 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
547 if (use_pager || use_pipe)
548 {
549 if (use_pager && ((fd_pager = mutt_file_open(mutt_buffer_string(pagerfile),
550 O_CREAT | O_EXCL | O_WRONLY)) == -1))
551 {
552 mutt_perror("open");
553 goto return_error;
554 }
555 unlink_pagerfile = true;
556
557 if (use_pipe && ((fd_temp = open(mutt_buffer_string(tmpfile), 0)) == -1))
558 {
559 if (fd_pager != -1)
560 close(fd_pager);
561 mutt_perror("open");
562 goto return_error;
563 }
564 unlink_pagerfile = true;
565
566 pid = filter_create_fd(mutt_buffer_string(cmd), NULL, NULL, NULL,
567 use_pipe ? fd_temp : -1, use_pager ? fd_pager : -1, -1);
568
569 if (pid == -1)
570 {
571 if (fd_pager != -1)
572 close(fd_pager);
573
574 if (fd_temp != -1)
575 close(fd_temp);
576
577 mutt_error(_("Can't create filter"));
578 goto return_error;
579 }
580
581 if (use_pager)
582 {
583 if (a->description)
584 {
585 snprintf(desc, sizeof(desc), _("---Command: %-20.20s Description: %s"),
587 }
588 else
589 {
590 snprintf(desc, sizeof(desc), _("---Command: %-30.30s Attachment: %s"),
591 mutt_buffer_string(cmd), type);
592 }
593 filter_wait(pid);
594 }
595 else
596 {
597 if (wait_interactive_filter(pid) || (entry->needsterminal && c_wait_key))
599 }
600
601 if (fd_temp != -1)
602 close(fd_temp);
603 if (fd_pager != -1)
604 close(fd_pager);
605 }
606 else
607 {
608 /* interactive cmd */
609 int rv = mutt_system(mutt_buffer_string(cmd));
610 if (rv == -1)
611 mutt_debug(LL_DEBUG1, "Error running \"%s\"\n", cmd->data);
612
613 if ((rv != 0) || (entry->needsterminal && c_wait_key))
615 }
616 }
617 else
618 {
619 /* Don't use mailcap; the attachment is viewed in the pager */
620
621 if (mode == MUTT_VA_AS_TEXT)
622 {
623 /* just let me see the raw data */
624 if (fp)
625 {
626 /* Viewing from a received message.
627 *
628 * Don't use mutt_save_attachment() because we want to perform charset
629 * conversion since this will be displayed by the internal pager. */
630 struct State state = { 0 };
631
632 state.fp_out = mutt_file_fopen(mutt_buffer_string(pagerfile), "w");
633 if (!state.fp_out)
634 {
635 mutt_debug(LL_DEBUG1, "mutt_file_fopen(%s) errno=%d %s\n",
636 mutt_buffer_string(pagerfile), errno, strerror(errno));
638 goto return_error;
639 }
640 state.fp_in = fp;
641 state.flags = STATE_CHARCONV;
642 mutt_decode_attachment(a, &state);
643 if (mutt_file_fclose(&state.fp_out) == EOF)
644 {
645 mutt_debug(LL_DEBUG1, "fclose(%s) errno=%d %s\n",
646 mutt_buffer_string(pagerfile), errno, strerror(errno));
647 }
648 }
649 else
650 {
651 /* in compose mode, just copy the file. we can't use
652 * mutt_decode_attachment() since it assumes the content-encoding has
653 * already been applied */
654 if (mutt_save_attachment(fp, a, mutt_buffer_string(pagerfile), MUTT_SAVE_NO_FLAGS, NULL))
655 goto return_error;
656 unlink_pagerfile = true;
657 }
659 }
660 else
661 {
663 const char *const c_pager = cs_subset_string(NeoMutt->sub, "pager");
664 if (!c_pager || mutt_str_equal(c_pager, "builtin"))
666
667 /* Use built-in handler */
669 {
670 goto return_error;
671 }
672 unlink_pagerfile = true;
673 }
674
675 if (a->description)
676 mutt_str_copy(desc, a->description, sizeof(desc));
677 else if (a->filename)
678 snprintf(desc, sizeof(desc), _("---Attachment: %s: %s"), a->filename, type);
679 else
680 snprintf(desc, sizeof(desc), _("---Attachment: %s"), type);
681 }
682
683 /* We only reach this point if there have been no errors */
684
685 if (use_pager)
686 {
687 struct PagerData pdata = { 0 };
688 struct PagerView pview = { &pdata };
689
690 pdata.actx = actx;
691 pdata.body = a;
692 pdata.fname = mutt_buffer_string(pagerfile);
693 pdata.fp = fp;
694
695 pview.banner = desc;
697 (is_message ? MUTT_PAGER_MESSAGE : MUTT_PAGER_NO_FLAGS) |
698 ((use_mailcap && entry->xneomuttnowrap) ? MUTT_PAGER_NOWRAP :
700 pview.mode = PAGER_MODE_ATTACH;
701
702 rc = mutt_do_pager(&pview, e);
703
704 mutt_buffer_reset(pagerfile);
705 unlink_pagerfile = false;
706 }
707 else
708 {
709 rc = 0;
710 }
711
712return_error:
713
714 if (!entry || !entry->xneomuttkeep)
715 {
716 if ((fp && !mutt_buffer_is_empty(tmpfile)) || has_tempfile)
717 {
718 /* add temporary file to TempAttachmentsList to be deleted on timeout hook */
720 }
721 }
722
723 mailcap_entry_free(&entry);
724
725 if (unlink_pagerfile)
727
728 mutt_buffer_pool_release(&tmpfile);
729 mutt_buffer_pool_release(&pagerfile);
731 mutt_envlist_unset("COLUMNS");
732
733 return rc;
734}
735
745int mutt_pipe_attachment(FILE *fp, struct Body *b, const char *path, char *outfile)
746{
747 pid_t pid = 0;
748 int out = -1, rc = 0;
749 bool is_flowed = false;
750 bool unlink_unstuff = false;
751 FILE *fp_filter = NULL, *fp_unstuff = NULL, *fp_in = NULL;
752 struct Buffer *unstuff_tempfile = NULL;
753
754 if (outfile && *outfile)
755 {
756 out = mutt_file_open(outfile, O_CREAT | O_EXCL | O_WRONLY);
757 if (out < 0)
758 {
759 mutt_perror("open");
760 return 0;
761 }
762 }
763
765 {
766 is_flowed = true;
767 unstuff_tempfile = mutt_buffer_pool_get();
768 mutt_buffer_mktemp(unstuff_tempfile);
769 }
770
771 mutt_endwin();
772
773 if (outfile && *outfile)
774 pid = filter_create_fd(path, &fp_filter, NULL, NULL, -1, out, -1);
775 else
776 pid = filter_create(path, &fp_filter, NULL, NULL);
777 if (pid < 0)
778 {
779 mutt_perror(_("Can't create filter"));
780 goto bail;
781 }
782
783 /* recv case */
784 if (fp)
785 {
786 struct State state = { 0 };
787
788 /* perform charset conversion on text attachments when piping */
789 state.flags = STATE_CHARCONV;
790
791 if (is_flowed)
792 {
793 fp_unstuff = mutt_file_fopen(mutt_buffer_string(unstuff_tempfile), "w");
794 if (fp_unstuff == NULL)
795 {
796 mutt_perror("mutt_file_fopen");
797 goto bail;
798 }
799 unlink_unstuff = true;
800
801 state.fp_in = fp;
802 state.fp_out = fp_unstuff;
803 mutt_decode_attachment(b, &state);
804 mutt_file_fclose(&fp_unstuff);
805
807
808 fp_unstuff = mutt_file_fopen(mutt_buffer_string(unstuff_tempfile), "r");
809 if (fp_unstuff == NULL)
810 {
811 mutt_perror("mutt_file_fopen");
812 goto bail;
813 }
814 mutt_file_copy_stream(fp_unstuff, fp_filter);
815 mutt_file_fclose(&fp_unstuff);
816 }
817 else
818 {
819 state.fp_in = fp;
820 state.fp_out = fp_filter;
821 mutt_decode_attachment(b, &state);
822 }
823 }
824
825 /* send case */
826 else
827 {
828 const char *infile = NULL;
829
830 if (is_flowed)
831 {
832 if (mutt_save_attachment(fp, b, mutt_buffer_string(unstuff_tempfile),
833 MUTT_SAVE_NO_FLAGS, NULL) == -1)
834 {
835 goto bail;
836 }
837 unlink_unstuff = true;
839 infile = mutt_buffer_string(unstuff_tempfile);
840 }
841 else
842 {
843 infile = b->filename;
844 }
845
846 fp_in = fopen(infile, "r");
847 if (!fp_in)
848 {
849 mutt_perror("fopen");
850 goto bail;
851 }
852
853 mutt_file_copy_stream(fp_in, fp_filter);
855 }
856
857 mutt_file_fclose(&fp_filter);
858 rc = 1;
859
860bail:
861 if (outfile && *outfile)
862 {
863 close(out);
864 if (rc == 0)
865 unlink(outfile);
866 else if (is_flowed)
868 }
869
870 mutt_file_fclose(&fp_unstuff);
871 mutt_file_fclose(&fp_filter);
873
874 if (unlink_unstuff)
875 mutt_file_unlink(mutt_buffer_string(unstuff_tempfile));
876 mutt_buffer_pool_release(&unstuff_tempfile);
877
878 /* check for error exit from child process */
879 if ((pid > 0) && (filter_wait(pid) != 0))
880 rc = 0;
881
882 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
883 if ((rc == 0) || c_wait_key)
885 return rc;
886}
887
894static FILE *save_attachment_open(const char *path, enum SaveAttach opt)
895{
896 if (opt == MUTT_SAVE_APPEND)
897 return fopen(path, "a");
898 if (opt == MUTT_SAVE_OVERWRITE)
899 return fopen(path, "w");
900
901 return mutt_file_fopen(path, "w");
902}
903
914int mutt_save_attachment(FILE *fp, struct Body *m, const char *path,
915 enum SaveAttach opt, struct Email *e)
916{
917 if (!m)
918 return -1;
919
920 if (fp)
921 {
922 /* recv mode */
923
924 if (e && m->email && (m->encoding != ENC_BASE64) &&
926 {
927 /* message type attachments are written to mail folders. */
928
929 char buf[8192] = { 0 };
930 struct Message *msg = NULL;
932 int rc = -1;
933
934 struct Email *e_new = m->email;
935 e_new->msgno = e->msgno; /* required for MH/maildir */
936 e_new->read = true;
937
938 if (!mutt_file_seek(fp, m->offset, SEEK_SET))
939 return -1;
940 if (!fgets(buf, sizeof(buf), fp))
941 return -1;
942 struct Mailbox *m_att = mx_path_resolve(path);
943 if (!mx_mbox_open(m_att, MUTT_APPEND | MUTT_QUIET))
944 {
945 mailbox_free(&m_att);
946 return -1;
947 }
948 msg = mx_msg_open_new(m_att, e_new,
949 is_from(buf, NULL, 0, NULL) ? MUTT_MSG_NO_FLAGS : MUTT_ADD_FROM);
950 if (!msg)
951 {
952 mx_mbox_close(m_att);
953 return -1;
954 }
955 if ((m_att->type == MUTT_MBOX) || (m_att->type == MUTT_MMDF))
956 chflags = CH_FROM | CH_UPDATE_LEN;
957 chflags |= ((m_att->type == MUTT_MAILDIR) ? CH_NOSTATUS : CH_UPDATE);
958 if ((mutt_copy_message_fp(msg->fp, fp, e_new, MUTT_CM_NO_FLAGS, chflags, 0) == 0) &&
959 (mx_msg_commit(m_att, msg) == 0))
960 {
961 rc = 0;
962 }
963 else
964 {
965 rc = -1;
966 }
967
968 mx_msg_close(m_att, &msg);
969 mx_mbox_close(m_att);
970 return rc;
971 }
972 else
973 {
974 /* In recv mode, extract from folder and decode */
975
976 struct State state = { 0 };
977
978 state.fp_out = save_attachment_open(path, opt);
979 if (!state.fp_out)
980 {
981 mutt_perror("fopen");
982 return -1;
983 }
984 if (!mutt_file_seek((state.fp_in = fp), m->offset, SEEK_SET))
985 {
986 mutt_file_fclose(&state.fp_out);
987 return -1;
988 }
989 mutt_decode_attachment(m, &state);
990
991 if (mutt_file_fsync_close(&state.fp_out) != 0)
992 {
993 mutt_perror("fclose");
994 return -1;
995 }
996 }
997 }
998 else
999 {
1000 if (!m->filename)
1001 return -1;
1002
1003 /* In send mode, just copy file */
1004
1005 FILE *fp_old = fopen(m->filename, "r");
1006 if (!fp_old)
1007 {
1008 mutt_perror("fopen");
1009 return -1;
1010 }
1011
1012 FILE *fp_new = save_attachment_open(path, opt);
1013 if (!fp_new)
1014 {
1015 mutt_perror("fopen");
1016 mutt_file_fclose(&fp_old);
1017 return -1;
1018 }
1019
1020 if (mutt_file_copy_stream(fp_old, fp_new) == -1)
1021 {
1022 mutt_error(_("Write fault"));
1023 mutt_file_fclose(&fp_old);
1024 mutt_file_fclose(&fp_new);
1025 return -1;
1026 }
1027 mutt_file_fclose(&fp_old);
1028 if (mutt_file_fsync_close(&fp_new) != 0)
1029 {
1030 mutt_error(_("Write fault"));
1031 return -1;
1032 }
1033 }
1034
1035 return 0;
1036}
1037
1048int mutt_decode_save_attachment(FILE *fp, struct Body *m, const char *path,
1049 StateFlags flags, enum SaveAttach opt)
1050{
1051 struct State state = { 0 };
1052 unsigned int saved_encoding = 0;
1053 struct Body *saved_parts = NULL;
1054 struct Email *e_saved = NULL;
1055 int rc = 0;
1056
1057 state.flags = flags;
1058
1059 if (opt == MUTT_SAVE_APPEND)
1060 state.fp_out = fopen(path, "a");
1061 else if (opt == MUTT_SAVE_OVERWRITE)
1062 state.fp_out = fopen(path, "w");
1063 else
1064 state.fp_out = mutt_file_fopen(path, "w");
1065
1066 if (!state.fp_out)
1067 {
1068 mutt_perror("fopen");
1069 return -1;
1070 }
1071
1072 if (!fp)
1073 {
1074 /* When called from the compose menu, the attachment isn't parsed,
1075 * so we need to do it here. */
1076 state.fp_in = fopen(m->filename, "r");
1077 if (!state.fp_in)
1078 {
1079 mutt_perror("fopen");
1080 mutt_file_fclose(&state.fp_out);
1081 return -1;
1082 }
1083
1084 struct stat st = { 0 };
1085 if (fstat(fileno(state.fp_in), &st) == -1)
1086 {
1087 mutt_perror("stat");
1088 mutt_file_fclose(&state.fp_in);
1089 mutt_file_fclose(&state.fp_out);
1090 return -1;
1091 }
1092
1093 saved_encoding = m->encoding;
1094 if (!is_multipart(m))
1095 m->encoding = ENC_8BIT;
1096
1097 m->length = st.st_size;
1098 m->offset = 0;
1099 saved_parts = m->parts;
1100 e_saved = m->email;
1101 mutt_parse_part(state.fp_in, m);
1102
1103 if (m->noconv || is_multipart(m))
1104 state.flags |= STATE_CHARCONV;
1105 }
1106 else
1107 {
1108 state.fp_in = fp;
1109 state.flags |= STATE_CHARCONV;
1110 }
1111
1112 mutt_body_handler(m, &state);
1113
1114 if (mutt_file_fsync_close(&state.fp_out) != 0)
1115 {
1116 mutt_perror("fclose");
1117 rc = -1;
1118 }
1119 if (!fp)
1120 {
1121 m->length = 0;
1122 m->encoding = saved_encoding;
1123 if (saved_parts)
1124 {
1125 email_free(&m->email);
1126 m->parts = saved_parts;
1127 m->email = e_saved;
1128 }
1129 mutt_file_fclose(&state.fp_in);
1130 }
1131
1132 return rc;
1133}
1134
1148int mutt_print_attachment(FILE *fp, struct Body *a)
1149{
1150 char type[256] = { 0 };
1151 pid_t pid;
1152 FILE *fp_in = NULL, *fp_out = NULL;
1153 bool unlink_newfile = false;
1154 struct Buffer *newfile = mutt_buffer_pool_get();
1155 struct Buffer *cmd = mutt_buffer_pool_get();
1156
1157 int rc = 0;
1158
1159 snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
1160
1161 if (mailcap_lookup(a, type, sizeof(type), NULL, MUTT_MC_PRINT))
1162 {
1163 mutt_debug(LL_DEBUG2, "Using mailcap\n");
1164
1165 struct MailcapEntry *entry = mailcap_entry_new();
1166 mailcap_lookup(a, type, sizeof(type), entry, MUTT_MC_PRINT);
1167
1168 char *sanitized_fname = mutt_str_dup(a->filename);
1169 /* In send mode (!fp), we allow slashes because those are part of
1170 * the tempfile. The path will be removed in expand_filename */
1171 mutt_file_sanitize_filename(sanitized_fname, fp ? true : false);
1172 mailcap_expand_filename(entry->nametemplate, sanitized_fname, newfile);
1173 FREE(&sanitized_fname);
1174
1175 if (mutt_save_attachment(fp, a, mutt_buffer_string(newfile),
1176 MUTT_SAVE_NO_FLAGS, NULL) == -1)
1177 {
1178 goto mailcap_cleanup;
1179 }
1180 unlink_newfile = 1;
1181
1183
1184 mutt_buffer_strcpy(cmd, entry->printcommand);
1185
1186 bool piped = mailcap_expand_command(a, mutt_buffer_string(newfile), type, cmd);
1187
1188 mutt_endwin();
1189
1190 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
1191 /* interactive program */
1192 if (piped)
1193 {
1194 fp_in = fopen(mutt_buffer_string(newfile), "r");
1195 if (!fp_in)
1196 {
1197 mutt_perror("fopen");
1198 mailcap_entry_free(&entry);
1199 goto mailcap_cleanup;
1200 }
1201
1202 pid = filter_create(mutt_buffer_string(cmd), &fp_out, NULL, NULL);
1203 if (pid < 0)
1204 {
1205 mutt_perror(_("Can't create filter"));
1206 mailcap_entry_free(&entry);
1207 mutt_file_fclose(&fp_in);
1208 goto mailcap_cleanup;
1209 }
1210 mutt_file_copy_stream(fp_in, fp_out);
1211 mutt_file_fclose(&fp_out);
1212 mutt_file_fclose(&fp_in);
1213 if (filter_wait(pid) || c_wait_key)
1215 }
1216 else
1217 {
1218 int rc2 = mutt_system(mutt_buffer_string(cmd));
1219 if (rc2 == -1)
1220 mutt_debug(LL_DEBUG1, "Error running \"%s\"\n", cmd->data);
1221
1222 if ((rc2 != 0) || c_wait_key)
1224 }
1225
1226 rc = 1;
1227
1228 mailcap_cleanup:
1229 if (unlink_newfile)
1231
1232 mailcap_entry_free(&entry);
1233 goto out;
1234 }
1235
1236 const char *const c_print_command = cs_subset_string(NeoMutt->sub, "print_command");
1237 if (mutt_istr_equal("text/plain", type) || mutt_istr_equal("application/postscript", type))
1238 {
1239 rc = (mutt_pipe_attachment(fp, a, NONULL(c_print_command), NULL));
1240 goto out;
1241 }
1242 else if (mutt_can_decode(a))
1243 {
1244 /* decode and print */
1245
1246 fp_in = NULL;
1247 fp_out = NULL;
1248
1249 mutt_buffer_mktemp(newfile);
1252 {
1253 unlink_newfile = true;
1254 mutt_debug(LL_DEBUG2, "successfully decoded %s type attachment to %s\n",
1255 type, mutt_buffer_string(newfile));
1256
1257 fp_in = fopen(mutt_buffer_string(newfile), "r");
1258 if (!fp_in)
1259 {
1260 mutt_perror("fopen");
1261 goto decode_cleanup;
1262 }
1263
1264 mutt_debug(LL_DEBUG2, "successfully opened %s read-only\n",
1265 mutt_buffer_string(newfile));
1266
1267 mutt_endwin();
1268 pid = filter_create(NONULL(c_print_command), &fp_out, NULL, NULL);
1269 if (pid < 0)
1270 {
1271 mutt_perror(_("Can't create filter"));
1272 goto decode_cleanup;
1273 }
1274
1275 mutt_debug(LL_DEBUG2, "Filter created\n");
1276
1277 mutt_file_copy_stream(fp_in, fp_out);
1278
1279 mutt_file_fclose(&fp_out);
1280 mutt_file_fclose(&fp_in);
1281
1282 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
1283 if ((filter_wait(pid) != 0) || c_wait_key)
1285 rc = 1;
1286 }
1287 decode_cleanup:
1288 mutt_file_fclose(&fp_in);
1289 mutt_file_fclose(&fp_out);
1290 if (unlink_newfile)
1292 }
1293 else
1294 {
1295 mutt_error(_("I don't know how to print that"));
1296 rc = 0;
1297 }
1298
1299out:
1300 mutt_buffer_pool_release(&newfile);
1302
1303 return rc;
1304}
1305
1310void mutt_add_temp_attachment(const char *filename)
1311{
1313}
1314
1319{
1320 struct ListNode *np = NULL;
1321
1322 STAILQ_FOREACH(np, &TempAttachmentsList, entries)
1323 {
1324 (void) mutt_file_chmod_add(np->data, S_IWUSR);
1326 }
1327
1329}
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:298
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:365
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:85
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:78
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:641
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:133
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition: curs_lib.c:311
int mutt_any_key_to_continue(const char *s)
Prompt the user to 'press any key' and wait.
Definition: curs_lib.c:386
void mutt_endwin(void)
Shutdown curses.
Definition: curs_lib.c:353
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:123
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:259
int mutt_file_open(const char *path, uint32_t flags)
Open a file.
Definition: file.c:548
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:634
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:149
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:665
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:708
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition: file.c:1390
int mutt_file_symlink(const char *oldpath, const char *newpath)
Create a symlink.
Definition: file.c:287
int mutt_file_chmod_add(const char *path, mode_t mode)
Add permissions to a file.
Definition: file.c:1110
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:193
int mutt_file_fsync_close(FILE **fp)
Flush the data, before closing a file (and NULL the pointer)
Definition: file.c:165
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
struct ListHead TempAttachmentsList
List of temporary files for displaying attachments.
Definition: globals.c:53
struct ListHead MimeLookupList
List of mime types that that shouldn't use the mailcap entry.
Definition: globals.c:51
#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:1849
int mutt_body_handler(struct Body *b, struct State *state)
Handler for the Body of an email.
Definition: handler.c:1619
void mutt_decode_attachment(struct Body *b, struct State *state)
Decode an email's attachment.
Definition: handler.c:1889
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:965
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
@ 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
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 mailcap_entry_free(struct MailcapEntry **ptr)
Deallocate an struct MailcapEntry.
Definition: mailcap.c:446
struct MailcapEntry * mailcap_entry_new(void)
Allocate memory for a new rfc1524 entry.
Definition: mailcap.c:437
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:544
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 STATE_PAGER
Output will be displayed in the Pager.
Definition: state.h:41
#define STATE_DISPLAY
Output is displayed to the user.
Definition: state.h:32
#define STATE_DISPLAY_ATTACH
We are displaying an attachment.
Definition: state.h:40
#define STATE_CHARCONV
Do character set conversions.
Definition: state.h:36
uint16_t StateFlags
Flags for State->flags, e.g. STATE_DISPLAY.
Definition: state.h:30
#define STATE_PRINTING
Are we printing? - STATE_DISPLAY "light".
Definition: state.h:37
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:819
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:807
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
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:652
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:524
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:326
int mutt_pipe_attachment(FILE *fp, struct Body *b, const char *path, char *outfile)
Pipe an attachment to a command.
Definition: mutt_attach.c:745
bool mutt_edit_attachment(struct Body *a)
Edit an attachment.
Definition: mutt_attach.c:264
void mutt_check_lookup_list(struct Body *b, char *type, size_t len)
Update the mime type.
Definition: mutt_attach.c:342
int mutt_decode_save_attachment(FILE *fp, struct Body *m, const char *path, StateFlags flags, enum SaveAttach opt)
Decode, then save an attachment.
Definition: mutt_attach.c:1048
int mutt_print_attachment(FILE *fp, struct Body *a)
Print out an attachment.
Definition: mutt_attach.c:1148
static int wait_interactive_filter(pid_t pid)
Wait after an interactive filter.
Definition: mutt_attach.c:389
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:1310
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:422
int mutt_compose_attachment(struct Body *a)
Create an attachment.
Definition: mutt_attach.c:117
void mutt_unlink_temp_attachments(void)
Delete all temporary attachments.
Definition: mutt_attach.c:1318
static FILE * save_attachment_open(const char *path, enum SaveAttach opt)
Open a file to write an attachment to.
Definition: mutt_attach.c:894
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:914
Handling of email attachments.
SaveAttach
Options for saving attachments.
Definition: mutt_attach.h:57
@ MUTT_SAVE_APPEND
Append to existing file.
Definition: mutt_attach.h:59
@ MUTT_SAVE_OVERWRITE
Overwrite existing file.
Definition: mutt_attach.h:60
@ MUTT_SAVE_NO_FLAGS
No flags set.
Definition: mutt_attach.h:58
ViewAttachMode
Options for mutt_view_attachment()
Definition: mutt_attach.h:43
@ MUTT_VA_MAILCAP
Force viewing using mailcap entry.
Definition: mutt_attach.h:45
@ MUTT_VA_REGULAR
View using default method.
Definition: mutt_attach.h:44
@ MUTT_VA_PAGER
View attachment in pager using copiousoutput mailcap.
Definition: mutt_attach.h:47
@ MUTT_VA_AS_TEXT
Force viewing as text.
Definition: mutt_attach.h:46
bool mutt_needs_mailcap(struct Body *m)
Does this type need a mailcap entry do display.
Definition: muttlib.c:408
void mutt_adv_mktemp(struct Buffer *buf)
Create a temporary file.
Definition: muttlib.c:83
Some miscellaneous functions.
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1200
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:305
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1062
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition: mx.c:1179
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1684
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition: mx.c:616
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:1745
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition: parse.c:1326
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1450
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:536
void mutt_rfc3676_space_unstuff_attachment(struct Body *b, const char *filename)
Unstuff attachments.
Definition: rfc3676.c:515
bool mutt_rfc3676_is_format_flowed(struct Body *b)
Is the Email "format-flowed"?
Definition: rfc3676.c:391
RFC3676 Format Flowed routines.
Convenience wrapper for the send headers.
void mutt_stamp_attachment(struct Body *a)
Timestamp an Attachment.
Definition: sendlib.c:403
enum ContentType mutt_lookup_mime_type(struct Body *att, const char *path)
Find the MIME type for an attachment.
Definition: sendlib.c:69
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:110
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:47
StateFlags flags
Flags, e.g. STATE_DISPLAY.
Definition: state.h:51
FILE * fp_out
File to write to.
Definition: state.h:49
FILE * fp_in
File to read from.
Definition: state.h:48
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:60
#define mutt_buffer_mktemp(buf)
Definition: tmp.h:37