NeoMutt  2023-11-03-85-g512e01
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
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 "imap/lib.h"
47#include "ncrypt/lib.h"
48#include "pager/lib.h"
49#include "question/lib.h"
50#include "send/lib.h"
51#include "attach.h"
52#include "cid.h"
53#include "copy.h"
54#include "globals.h"
55#include "handler.h"
56#include "mailcap.h"
57#include "muttlib.h"
58#include "mx.h"
59#include "protos.h"
60#include "rfc3676.h"
61
69{
70 char type[256] = { 0 };
71
72 if (b->unlink)
73 return 0;
74
75 struct Buffer *tmpfile = buf_pool_get();
76 struct MailcapEntry *entry = mailcap_entry_new();
77 snprintf(type, sizeof(type), "%s/%s", TYPE(b), b->subtype);
78 mailcap_lookup(b, type, sizeof(type), entry, MUTT_MC_NO_FLAGS);
80
81 mailcap_entry_free(&entry);
82
83 FILE *fp_in = NULL, *fp_out = NULL;
84 if ((fp_in = fopen(b->filename, "r")) &&
85 (fp_out = mutt_file_fopen(buf_string(tmpfile), "w")))
86 {
87 mutt_file_copy_stream(fp_in, fp_out);
89 b->unlink = true;
90
91 struct stat st = { 0 };
92 if ((fstat(fileno(fp_in), &st) == 0) && (b->stamp >= st.st_mtime))
93 {
95 }
96 }
97 else
98 {
99 mutt_perror("%s", fp_in ? buf_string(tmpfile) : b->filename);
100 }
101
102 mutt_file_fclose(&fp_in);
103 mutt_file_fclose(&fp_out);
104
105 buf_pool_release(&tmpfile);
106
107 return b->unlink ? 0 : -1;
108}
109
117{
118 char type[256] = { 0 };
119 struct MailcapEntry *entry = mailcap_entry_new();
120 bool unlink_newfile = false;
121 int rc = 0;
122 struct Buffer *cmd = buf_pool_get();
123 struct Buffer *newfile = buf_pool_get();
124 struct Buffer *tmpfile = buf_pool_get();
125
126 snprintf(type, sizeof(type), "%s/%s", TYPE(b), b->subtype);
127 if (mailcap_lookup(b, type, sizeof(type), entry, MUTT_MC_COMPOSE))
128 {
129 if (entry->composecommand || entry->composetypecommand)
130 {
131 if (entry->composetypecommand)
132 buf_strcpy(cmd, entry->composetypecommand);
133 else
134 buf_strcpy(cmd, entry->composecommand);
135
136 mailcap_expand_filename(entry->nametemplate, b->filename, newfile);
137 mutt_debug(LL_DEBUG1, "oldfile: %s newfile: %s\n", b->filename,
138 buf_string(newfile));
139 if (mutt_file_symlink(b->filename, buf_string(newfile)) == -1)
140 {
141 if (query_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
142 goto bailout;
143 buf_strcpy(newfile, b->filename);
144 }
145 else
146 {
147 unlink_newfile = true;
148 }
149
150 if (mailcap_expand_command(b, buf_string(newfile), type, cmd))
151 {
152 /* For now, editing requires a file, no piping */
153 mutt_error(_("Mailcap compose entry requires %%s"));
154 }
155 else
156 {
157 int r;
158
159 mutt_endwin();
160 r = mutt_system(buf_string(cmd));
161 if (r == -1)
162 mutt_error(_("Error running \"%s\""), buf_string(cmd));
163
164 if ((r != -1) && entry->composetypecommand)
165 {
166 FILE *fp = mutt_file_fopen(b->filename, "r");
167 if (!fp)
168 {
169 mutt_perror(_("Failure to open file to parse headers"));
170 goto bailout;
171 }
172
173 struct Body *b_mime = mutt_read_mime_header(fp, 0);
174 if (b_mime)
175 {
176 if (!TAILQ_EMPTY(&b_mime->parameter))
177 {
179 b->parameter = b_mime->parameter;
180 TAILQ_INIT(&b_mime->parameter);
181 }
182 if (b_mime->description)
183 {
184 FREE(&b->description);
185 b->description = b_mime->description;
186 b_mime->description = NULL;
187 }
188 if (b_mime->form_name)
189 {
190 FREE(&b->form_name);
191 b->form_name = b_mime->form_name;
192 b_mime->form_name = NULL;
193 }
194
195 /* Remove headers by copying out data to another file, then
196 * copying the file back */
197 const LOFF_T offset = b_mime->offset;
198 mutt_body_free(&b_mime);
199 if (!mutt_file_seek(fp, offset, SEEK_SET))
200 {
201 goto bailout;
202 }
203
204 buf_mktemp(tmpfile);
205 FILE *fp_tmp = mutt_file_fopen(buf_string(tmpfile), "w");
206 if (!fp_tmp)
207 {
208 mutt_perror(_("Failure to open file to strip headers"));
209 mutt_file_fclose(&fp);
210 goto bailout;
211 }
212 mutt_file_copy_stream(fp, fp_tmp);
213 mutt_file_fclose(&fp);
214 mutt_file_fclose(&fp_tmp);
216 if (mutt_file_rename(buf_string(tmpfile), b->filename) != 0)
217 {
218 mutt_perror(_("Failure to rename file"));
219 goto bailout;
220 }
221 }
222 }
223 }
224 }
225 }
226 else
227 {
228 mutt_message(_("No mailcap compose entry for %s, creating empty file"), type);
229 rc = 1;
230 goto bailout;
231 }
232
233 rc = 1;
234
235bailout:
236
237 if (unlink_newfile)
238 unlink(buf_string(newfile));
239
240 buf_pool_release(&cmd);
241 buf_pool_release(&newfile);
242 buf_pool_release(&tmpfile);
243
244 mailcap_entry_free(&entry);
245 return rc;
246}
247
262{
263 char type[256] = { 0 };
264 struct MailcapEntry *entry = mailcap_entry_new();
265 bool unlink_newfile = false;
266 bool rc = false;
267 struct Buffer *cmd = buf_pool_get();
268 struct Buffer *newfile = buf_pool_get();
269
270 snprintf(type, sizeof(type), "%s/%s", TYPE(b), b->subtype);
271 if (mailcap_lookup(b, type, sizeof(type), entry, MUTT_MC_EDIT))
272 {
273 if (entry->editcommand)
274 {
275 buf_strcpy(cmd, entry->editcommand);
276 mailcap_expand_filename(entry->nametemplate, b->filename, newfile);
277 mutt_debug(LL_DEBUG1, "oldfile: %s newfile: %s\n", b->filename,
278 buf_string(newfile));
279 if (mutt_file_symlink(b->filename, buf_string(newfile)) == -1)
280 {
281 if (query_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
282 goto bailout;
283 buf_strcpy(newfile, b->filename);
284 }
285 else
286 {
287 unlink_newfile = true;
288 }
289
290 if (mailcap_expand_command(b, buf_string(newfile), type, cmd))
291 {
292 /* For now, editing requires a file, no piping */
293 mutt_error(_("Mailcap Edit entry requires %%s"));
294 goto bailout;
295 }
296 else
297 {
298 mutt_endwin();
299 if (mutt_system(buf_string(cmd)) == -1)
300 {
301 mutt_error(_("Error running \"%s\""), buf_string(cmd));
302 goto bailout;
303 }
304 }
305 }
306 }
307 else if (b->type == TYPE_TEXT)
308 {
309 /* On text, default to editor */
310 const char *const c_editor = cs_subset_string(NeoMutt->sub, "editor");
311 mutt_edit_file(NONULL(c_editor), b->filename);
312 }
313 else
314 {
315 mutt_error(_("No mailcap edit entry for %s"), type);
316 goto bailout;
317 }
318
319 rc = true;
320
321bailout:
322
323 if (unlink_newfile)
324 unlink(buf_string(newfile));
325
326 buf_pool_release(&cmd);
327 buf_pool_release(&newfile);
328
329 mailcap_entry_free(&entry);
330 return rc;
331}
332
339void mutt_check_lookup_list(struct Body *b, char *type, size_t len)
340{
341 struct ListNode *np = NULL;
342 STAILQ_FOREACH(np, &MimeLookupList, entries)
343 {
344 const int i = mutt_str_len(np->data) - 1;
345 if (((i > 0) && (np->data[i - 1] == '/') && (np->data[i] == '*') &&
346 mutt_istrn_equal(type, np->data, i)) ||
347 mutt_istr_equal(type, np->data))
348 {
349 struct Body tmp = { 0 };
350 enum ContentType n;
351 if ((n = mutt_lookup_mime_type(&tmp, b->filename)) != TYPE_OTHER ||
353 {
354 snprintf(type, len, "%s/%s",
355 (n == TYPE_AUDIO) ? "audio" :
356 (n == TYPE_APPLICATION) ? "application" :
357 (n == TYPE_IMAGE) ? "image" :
358 (n == TYPE_MESSAGE) ? "message" :
359 (n == TYPE_MODEL) ? "model" :
360 (n == TYPE_MULTIPART) ? "multipart" :
361 (n == TYPE_TEXT) ? "text" :
362 (n == TYPE_VIDEO) ? "video" :
363 "other",
364 tmp.subtype);
365 mutt_debug(LL_DEBUG1, "\"%s\" -> %s\n", b->filename, type);
366 }
367 FREE(&tmp.subtype);
368 FREE(&tmp.xtype);
369 }
370 }
371}
372
386static int wait_interactive_filter(pid_t pid)
387{
388 int rc;
389
390 rc = imap_wait_keep_alive(pid);
392 rc = WIFEXITED(rc) ? WEXITSTATUS(rc) : -1;
393
394 return rc;
395}
396
415int mutt_view_attachment(FILE *fp, struct Body *b, enum ViewAttachMode mode,
416 struct Email *e, struct AttachCtx *actx, struct MuttWindow *win)
417{
418 bool use_mailcap = false;
419 bool use_pipe = false;
420 bool use_pager = true;
421 char type[256] = { 0 };
422 char desc[256] = { 0 };
423 char *fname = NULL;
424 struct MailcapEntry *entry = NULL;
425 int rc = -1;
426 bool has_tempfile = false;
427 bool unlink_pagerfile = false;
428
429 bool is_message = mutt_is_message_type(b->type, b->subtype);
430 if ((WithCrypto != 0) && is_message && b->email &&
432 {
433 return rc;
434 }
435
436 struct Buffer *tmpfile = buf_pool_get();
437 struct Buffer *pagerfile = buf_pool_get();
438 struct Buffer *cmd = buf_pool_get();
439
440 use_mailcap = ((mode == MUTT_VA_MAILCAP) ||
441 ((mode == MUTT_VA_REGULAR) && mutt_needs_mailcap(b)) ||
442 (mode == MUTT_VA_PAGER));
443 snprintf(type, sizeof(type), "%s/%s", TYPE(b), b->subtype);
444
445 char columns[16] = { 0 };
446 snprintf(columns, sizeof(columns), "%d", win->state.cols);
447 envlist_set(&EnvList, "COLUMNS", columns, true);
448
449 if (use_mailcap)
450 {
451 entry = mailcap_entry_new();
452 enum MailcapLookup mailcap_opt = (mode == MUTT_VA_PAGER) ? MUTT_MC_AUTOVIEW : MUTT_MC_NO_FLAGS;
453 if (!mailcap_lookup(b, type, sizeof(type), entry, mailcap_opt))
454 {
455 if ((mode == MUTT_VA_REGULAR) || (mode == MUTT_VA_PAGER))
456 {
457 /* fallback to view as text */
458 mailcap_entry_free(&entry);
459 mutt_error(_("No matching mailcap entry found. Viewing as text."));
460 mode = MUTT_VA_AS_TEXT;
461 use_mailcap = false;
462 }
463 else
464 {
465 goto return_error;
466 }
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 buf_strcpy(cmd, entry->command);
478
479 fname = mutt_str_dup(b->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, b, buf_string(tmpfile), 0, NULL) == -1)
487 goto return_error;
488 has_tempfile = true;
489
491
492 /* check for multipart/related and save attachments with b 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], b, "related");
498 else
499 related_ancestor = attach_body_ancestor(e->body, b, "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(b, buf_string(tmpfile), type, cmd);
514 use_pager = entry->copiousoutput;
515 }
516
517 if (use_pager)
518 {
519 if (fp && !use_mailcap && b->filename)
520 {
521 /* recv case */
522 buf_strcpy(pagerfile, b->filename);
523 mutt_adv_mktemp(pagerfile);
524 }
525 else
526 {
527 buf_mktemp(pagerfile);
528 }
529 }
530
531 if (use_mailcap)
532 {
533 pid_t pid = 0;
534 int fd_temp = -1, fd_pager = -1;
535
536 if (!use_pager)
537 mutt_endwin();
538
539 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
540 if (use_pager || use_pipe)
541 {
542 if (use_pager && ((fd_pager = mutt_file_open(buf_string(pagerfile),
543 O_CREAT | O_EXCL | O_WRONLY)) == -1))
544 {
545 mutt_perror("open");
546 goto return_error;
547 }
548 unlink_pagerfile = true;
549
550 if (use_pipe && ((fd_temp = open(buf_string(tmpfile), 0)) == -1))
551 {
552 if (fd_pager != -1)
553 close(fd_pager);
554 mutt_perror("open");
555 goto return_error;
556 }
557 unlink_pagerfile = true;
558
559 pid = filter_create_fd(buf_string(cmd), NULL, NULL, NULL, use_pipe ? fd_temp : -1,
560 use_pager ? fd_pager : -1, -1, EnvList);
561
562 if (pid == -1)
563 {
564 if (fd_pager != -1)
565 close(fd_pager);
566
567 if (fd_temp != -1)
568 close(fd_temp);
569
570 mutt_error(_("Can't create filter"));
571 goto return_error;
572 }
573
574 if (use_pager)
575 {
576 if (b->description)
577 {
578 snprintf(desc, sizeof(desc), _("---Command: %-20.20s Description: %s"),
579 buf_string(cmd), b->description);
580 }
581 else
582 {
583 snprintf(desc, sizeof(desc), _("---Command: %-30.30s Attachment: %s"),
584 buf_string(cmd), type);
585 }
586 filter_wait(pid);
587 }
588 else
589 {
590 if (wait_interactive_filter(pid) || (entry->needsterminal && c_wait_key))
592 }
593
594 if (fd_temp != -1)
595 close(fd_temp);
596 if (fd_pager != -1)
597 close(fd_pager);
598 }
599 else
600 {
601 /* interactive cmd */
602 int rv = mutt_system(buf_string(cmd));
603 if (rv == -1)
604 mutt_debug(LL_DEBUG1, "Error running \"%s\"\n", cmd->data);
605
606 if ((rv != 0) || (entry->needsterminal && c_wait_key))
608 }
609 }
610 else
611 {
612 /* Don't use mailcap; the attachment is viewed in the pager */
613
614 if (mode == MUTT_VA_AS_TEXT)
615 {
616 /* just let me see the raw data */
617 if (fp)
618 {
619 /* Viewing from a received message.
620 *
621 * Don't use mutt_save_attachment() because we want to perform charset
622 * conversion since this will be displayed by the internal pager. */
623 struct State state = { 0 };
624
625 state.fp_out = mutt_file_fopen(buf_string(pagerfile), "w");
626 if (!state.fp_out)
627 {
628 mutt_debug(LL_DEBUG1, "mutt_file_fopen(%s) errno=%d %s\n",
629 buf_string(pagerfile), errno, strerror(errno));
630 mutt_perror("%s", buf_string(pagerfile));
631 goto return_error;
632 }
633 state.fp_in = fp;
634 state.flags = STATE_CHARCONV;
635 mutt_decode_attachment(b, &state);
636 if (mutt_file_fclose(&state.fp_out) == EOF)
637 {
638 mutt_debug(LL_DEBUG1, "fclose(%s) errno=%d %s\n",
639 buf_string(pagerfile), errno, strerror(errno));
640 }
641 }
642 else
643 {
644 /* in compose mode, just copy the file. we can't use
645 * mutt_decode_attachment() since it assumes the content-encoding has
646 * already been applied */
647 if (mutt_save_attachment(fp, b, buf_string(pagerfile), MUTT_SAVE_NO_FLAGS, NULL))
648 goto return_error;
649 unlink_pagerfile = true;
650 }
652 }
653 else
654 {
656 const char *const c_pager = pager_get_pager(NeoMutt->sub);
657 if (!c_pager)
659
660 /* Use built-in handler */
662 {
663 goto return_error;
664 }
665 unlink_pagerfile = true;
666 }
667
668 if (b->description)
669 mutt_str_copy(desc, b->description, sizeof(desc));
670 else if (b->filename)
671 snprintf(desc, sizeof(desc), _("---Attachment: %s: %s"), b->filename, type);
672 else
673 snprintf(desc, sizeof(desc), _("---Attachment: %s"), type);
674 }
675
676 /* We only reach this point if there have been no errors */
677
678 if (use_pager)
679 {
680 struct PagerData pdata = { 0 };
681 struct PagerView pview = { &pdata };
682
683 pdata.actx = actx;
684 pdata.body = b;
685 pdata.fname = buf_string(pagerfile);
686 pdata.fp = fp;
687
688 pview.banner = desc;
690 (is_message ? MUTT_PAGER_MESSAGE : MUTT_PAGER_NO_FLAGS) |
691 ((use_mailcap && entry->xneomuttnowrap) ? MUTT_PAGER_NOWRAP :
693 pview.mode = PAGER_MODE_ATTACH;
694
695 rc = mutt_do_pager(&pview, e);
696
697 buf_reset(pagerfile);
698 unlink_pagerfile = false;
699 }
700 else
701 {
702 rc = 0;
703 }
704
705return_error:
706
707 if (!entry || !entry->xneomuttkeep)
708 {
709 if ((fp && !buf_is_empty(tmpfile)) || has_tempfile)
710 {
711 /* add temporary file to TempAttachmentsList to be deleted on timeout hook */
713 }
714 }
715
716 mailcap_entry_free(&entry);
717
718 if (unlink_pagerfile)
719 mutt_file_unlink(buf_string(pagerfile));
720
721 buf_pool_release(&tmpfile);
722 buf_pool_release(&pagerfile);
723 buf_pool_release(&cmd);
724 envlist_unset(&EnvList, "COLUMNS");
725
726 return rc;
727}
728
738int mutt_pipe_attachment(FILE *fp, struct Body *b, const char *path, const char *outfile)
739{
740 pid_t pid = 0;
741 int out = -1, rc = 0;
742 bool is_flowed = false;
743 bool unlink_unstuff = false;
744 FILE *fp_filter = NULL, *fp_unstuff = NULL, *fp_in = NULL;
745 struct Buffer *unstuff_tempfile = NULL;
746
747 if (outfile && *outfile)
748 {
749 out = mutt_file_open(outfile, O_CREAT | O_EXCL | O_WRONLY);
750 if (out < 0)
751 {
752 mutt_perror("open");
753 return 0;
754 }
755 }
756
758 {
759 is_flowed = true;
760 unstuff_tempfile = buf_pool_get();
761 buf_mktemp(unstuff_tempfile);
762 }
763
764 mutt_endwin();
765
766 if (outfile && *outfile)
767 pid = filter_create_fd(path, &fp_filter, NULL, NULL, -1, out, -1, EnvList);
768 else
769 pid = filter_create(path, &fp_filter, NULL, NULL, EnvList);
770 if (pid < 0)
771 {
772 mutt_perror(_("Can't create filter"));
773 goto bail;
774 }
775
776 /* recv case */
777 if (fp)
778 {
779 struct State state = { 0 };
780
781 /* perform charset conversion on text attachments when piping */
782 state.flags = STATE_CHARCONV;
783
784 if (is_flowed)
785 {
786 fp_unstuff = mutt_file_fopen(buf_string(unstuff_tempfile), "w");
787 if (!fp_unstuff)
788 {
789 mutt_perror("mutt_file_fopen");
790 goto bail;
791 }
792 unlink_unstuff = true;
793
794 state.fp_in = fp;
795 state.fp_out = fp_unstuff;
796 mutt_decode_attachment(b, &state);
797 mutt_file_fclose(&fp_unstuff);
798
800
801 fp_unstuff = mutt_file_fopen(buf_string(unstuff_tempfile), "r");
802 if (!fp_unstuff)
803 {
804 mutt_perror("mutt_file_fopen");
805 goto bail;
806 }
807 mutt_file_copy_stream(fp_unstuff, fp_filter);
808 mutt_file_fclose(&fp_unstuff);
809 }
810 else
811 {
812 state.fp_in = fp;
813 state.fp_out = fp_filter;
814 mutt_decode_attachment(b, &state);
815 }
816 }
817 else
818 {
819 /* send case */
820 const char *infile = NULL;
821
822 if (is_flowed)
823 {
824 if (mutt_save_attachment(fp, b, buf_string(unstuff_tempfile),
825 MUTT_SAVE_NO_FLAGS, NULL) == -1)
826 {
827 goto bail;
828 }
829 unlink_unstuff = true;
831 infile = buf_string(unstuff_tempfile);
832 }
833 else
834 {
835 infile = b->filename;
836 }
837
838 fp_in = fopen(infile, "r");
839 if (!fp_in)
840 {
841 mutt_perror("fopen");
842 goto bail;
843 }
844
845 mutt_file_copy_stream(fp_in, fp_filter);
847 }
848
849 mutt_file_fclose(&fp_filter);
850 rc = 1;
851
852bail:
853 if (outfile && *outfile)
854 {
855 close(out);
856 if (rc == 0)
857 unlink(outfile);
858 else if (is_flowed)
860 }
861
862 mutt_file_fclose(&fp_unstuff);
863 mutt_file_fclose(&fp_filter);
865
866 if (unlink_unstuff)
867 mutt_file_unlink(buf_string(unstuff_tempfile));
868 buf_pool_release(&unstuff_tempfile);
869
870 /* check for error exit from child process */
871 if ((pid > 0) && (filter_wait(pid) != 0))
872 rc = 0;
873
874 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
875 if ((rc == 0) || c_wait_key)
877 return rc;
878}
879
886static FILE *save_attachment_open(const char *path, enum SaveAttach opt)
887{
888 if (opt == MUTT_SAVE_APPEND)
889 return fopen(path, "a");
890 if (opt == MUTT_SAVE_OVERWRITE)
891 return fopen(path, "w");
892
893 return mutt_file_fopen(path, "w");
894}
895
906int mutt_save_attachment(FILE *fp, struct Body *b, const char *path,
907 enum SaveAttach opt, struct Email *e)
908{
909 if (!b)
910 return -1;
911
912 if (fp)
913 {
914 /* recv mode */
915
916 if (e && b->email && (b->encoding != ENC_BASE64) &&
918 {
919 /* message type attachments are written to mail folders. */
920
921 char buf[8192] = { 0 };
922 struct Message *msg = NULL;
924 int rc = -1;
925
926 struct Email *e_new = b->email;
927 e_new->msgno = e->msgno; /* required for MH/maildir */
928 e_new->read = true;
929
930 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
931 return -1;
932 if (!fgets(buf, sizeof(buf), fp))
933 return -1;
934 struct Mailbox *m_att = mx_path_resolve(path);
935 if (!mx_mbox_open(m_att, MUTT_APPEND | MUTT_QUIET))
936 {
937 mailbox_free(&m_att);
938 return -1;
939 }
940 msg = mx_msg_open_new(m_att, e_new,
941 is_from(buf, NULL, 0, NULL) ? MUTT_MSG_NO_FLAGS : MUTT_ADD_FROM);
942 if (!msg)
943 {
944 mx_mbox_close(m_att);
945 return -1;
946 }
947 if ((m_att->type == MUTT_MBOX) || (m_att->type == MUTT_MMDF))
948 chflags = CH_FROM | CH_UPDATE_LEN;
949 chflags |= ((m_att->type == MUTT_MAILDIR) ? CH_NOSTATUS : CH_UPDATE);
950 if ((mutt_copy_message_fp(msg->fp, fp, e_new, MUTT_CM_NO_FLAGS, chflags, 0) == 0) &&
951 (mx_msg_commit(m_att, msg) == 0))
952 {
953 rc = 0;
954 }
955 else
956 {
957 rc = -1;
958 }
959
960 mx_msg_close(m_att, &msg);
961 mx_mbox_close(m_att);
962 return rc;
963 }
964 else
965 {
966 /* In recv mode, extract from folder and decode */
967
968 struct State state = { 0 };
969
970 state.fp_out = save_attachment_open(path, opt);
971 if (!state.fp_out)
972 {
973 mutt_perror("fopen");
974 return -1;
975 }
976 if (!mutt_file_seek((state.fp_in = fp), b->offset, SEEK_SET))
977 {
978 mutt_file_fclose(&state.fp_out);
979 return -1;
980 }
981 mutt_decode_attachment(b, &state);
982
983 if (mutt_file_fsync_close(&state.fp_out) != 0)
984 {
985 mutt_perror("fclose");
986 return -1;
987 }
988 }
989 }
990 else
991 {
992 if (!b->filename)
993 return -1;
994
995 /* In send mode, just copy file */
996
997 FILE *fp_old = fopen(b->filename, "r");
998 if (!fp_old)
999 {
1000 mutt_perror("fopen");
1001 return -1;
1002 }
1003
1004 FILE *fp_new = save_attachment_open(path, opt);
1005 if (!fp_new)
1006 {
1007 mutt_perror("fopen");
1008 mutt_file_fclose(&fp_old);
1009 return -1;
1010 }
1011
1012 if (mutt_file_copy_stream(fp_old, fp_new) == -1)
1013 {
1014 mutt_error(_("Write fault"));
1015 mutt_file_fclose(&fp_old);
1016 mutt_file_fclose(&fp_new);
1017 return -1;
1018 }
1019 mutt_file_fclose(&fp_old);
1020 if (mutt_file_fsync_close(&fp_new) != 0)
1021 {
1022 mutt_error(_("Write fault"));
1023 return -1;
1024 }
1025 }
1026
1027 return 0;
1028}
1029
1040int mutt_decode_save_attachment(FILE *fp, struct Body *b, const char *path,
1041 StateFlags flags, enum SaveAttach opt)
1042{
1043 struct State state = { 0 };
1044 unsigned int saved_encoding = 0;
1045 struct Body *saved_parts = NULL;
1046 struct Email *e_saved = NULL;
1047 int rc = 0;
1048
1049 state.flags = flags;
1050
1051 if (opt == MUTT_SAVE_APPEND)
1052 state.fp_out = fopen(path, "a");
1053 else if (opt == MUTT_SAVE_OVERWRITE)
1054 state.fp_out = fopen(path, "w");
1055 else
1056 state.fp_out = mutt_file_fopen(path, "w");
1057
1058 if (!state.fp_out)
1059 {
1060 mutt_perror("fopen");
1061 return -1;
1062 }
1063
1064 if (fp)
1065 {
1066 state.fp_in = fp;
1067 state.flags |= STATE_CHARCONV;
1068 }
1069 else
1070 {
1071 /* When called from the compose menu, the attachment isn't parsed,
1072 * so we need to do it here. */
1073 state.fp_in = fopen(b->filename, "r");
1074 if (!state.fp_in)
1075 {
1076 mutt_perror("fopen");
1077 mutt_file_fclose(&state.fp_out);
1078 return -1;
1079 }
1080
1081 struct stat st = { 0 };
1082 if (fstat(fileno(state.fp_in), &st) == -1)
1083 {
1084 mutt_perror("stat");
1085 mutt_file_fclose(&state.fp_in);
1086 mutt_file_fclose(&state.fp_out);
1087 return -1;
1088 }
1089
1090 saved_encoding = b->encoding;
1091 if (!is_multipart(b))
1092 b->encoding = ENC_8BIT;
1093
1094 b->length = st.st_size;
1095 b->offset = 0;
1096 saved_parts = b->parts;
1097 e_saved = b->email;
1098 mutt_parse_part(state.fp_in, b);
1099
1100 if (b->noconv || is_multipart(b))
1101 state.flags |= STATE_CHARCONV;
1102 }
1103
1104 mutt_body_handler(b, &state);
1105
1106 if (mutt_file_fsync_close(&state.fp_out) != 0)
1107 {
1108 mutt_perror("fclose");
1109 rc = -1;
1110 }
1111 if (!fp)
1112 {
1113 b->length = 0;
1114 b->encoding = saved_encoding;
1115 if (saved_parts)
1116 {
1117 email_free(&b->email);
1118 b->parts = saved_parts;
1119 b->email = e_saved;
1120 }
1121 mutt_file_fclose(&state.fp_in);
1122 }
1123
1124 return rc;
1125}
1126
1140int mutt_print_attachment(FILE *fp, struct Body *b)
1141{
1142 char type[256] = { 0 };
1143 pid_t pid;
1144 FILE *fp_in = NULL, *fp_out = NULL;
1145 bool unlink_newfile = false;
1146 struct Buffer *newfile = buf_pool_get();
1147 struct Buffer *cmd = buf_pool_get();
1148
1149 int rc = 0;
1150
1151 snprintf(type, sizeof(type), "%s/%s", TYPE(b), b->subtype);
1152
1153 if (mailcap_lookup(b, type, sizeof(type), NULL, MUTT_MC_PRINT))
1154 {
1155 mutt_debug(LL_DEBUG2, "Using mailcap\n");
1156
1157 struct MailcapEntry *entry = mailcap_entry_new();
1158 mailcap_lookup(b, type, sizeof(type), entry, MUTT_MC_PRINT);
1159
1160 char *sanitized_fname = mutt_str_dup(b->filename);
1161 /* In send mode (!fp), we allow slashes because those are part of
1162 * the tempfile. The path will be removed in expand_filename */
1163 mutt_file_sanitize_filename(sanitized_fname, fp ? true : false);
1164 mailcap_expand_filename(entry->nametemplate, sanitized_fname, newfile);
1165 FREE(&sanitized_fname);
1166
1167 if (mutt_save_attachment(fp, b, buf_string(newfile), MUTT_SAVE_NO_FLAGS, NULL) == -1)
1168 {
1169 goto mailcap_cleanup;
1170 }
1171 unlink_newfile = 1;
1172
1174
1175 buf_strcpy(cmd, entry->printcommand);
1176
1177 bool piped = mailcap_expand_command(b, buf_string(newfile), type, cmd);
1178
1179 mutt_endwin();
1180
1181 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
1182 /* interactive program */
1183 if (piped)
1184 {
1185 fp_in = fopen(buf_string(newfile), "r");
1186 if (!fp_in)
1187 {
1188 mutt_perror("fopen");
1189 mailcap_entry_free(&entry);
1190 goto mailcap_cleanup;
1191 }
1192
1193 pid = filter_create(buf_string(cmd), &fp_out, NULL, NULL, EnvList);
1194 if (pid < 0)
1195 {
1196 mutt_perror(_("Can't create filter"));
1197 mailcap_entry_free(&entry);
1198 mutt_file_fclose(&fp_in);
1199 goto mailcap_cleanup;
1200 }
1201 mutt_file_copy_stream(fp_in, fp_out);
1202 mutt_file_fclose(&fp_out);
1203 mutt_file_fclose(&fp_in);
1204 if (filter_wait(pid) || c_wait_key)
1206 }
1207 else
1208 {
1209 int rc2 = mutt_system(buf_string(cmd));
1210 if (rc2 == -1)
1211 mutt_debug(LL_DEBUG1, "Error running \"%s\"\n", cmd->data);
1212
1213 if ((rc2 != 0) || c_wait_key)
1215 }
1216
1217 rc = 1;
1218
1219 mailcap_cleanup:
1220 if (unlink_newfile)
1221 mutt_file_unlink(buf_string(newfile));
1222
1223 mailcap_entry_free(&entry);
1224 goto out;
1225 }
1226
1227 const char *const c_print_command = cs_subset_string(NeoMutt->sub, "print_command");
1228 if (mutt_istr_equal("text/plain", type) || mutt_istr_equal("application/postscript", type))
1229 {
1230 rc = (mutt_pipe_attachment(fp, b, NONULL(c_print_command), NULL));
1231 goto out;
1232 }
1233 else if (mutt_can_decode(b))
1234 {
1235 /* decode and print */
1236
1237 fp_in = NULL;
1238 fp_out = NULL;
1239
1240 buf_mktemp(newfile);
1242 MUTT_SAVE_NO_FLAGS) == 0)
1243 {
1244 unlink_newfile = true;
1245 mutt_debug(LL_DEBUG2, "successfully decoded %s type attachment to %s\n",
1246 type, buf_string(newfile));
1247
1248 fp_in = fopen(buf_string(newfile), "r");
1249 if (!fp_in)
1250 {
1251 mutt_perror("fopen");
1252 goto decode_cleanup;
1253 }
1254
1255 mutt_debug(LL_DEBUG2, "successfully opened %s read-only\n", buf_string(newfile));
1256
1257 mutt_endwin();
1258 pid = filter_create(NONULL(c_print_command), &fp_out, NULL, NULL, EnvList);
1259 if (pid < 0)
1260 {
1261 mutt_perror(_("Can't create filter"));
1262 goto decode_cleanup;
1263 }
1264
1265 mutt_debug(LL_DEBUG2, "Filter created\n");
1266
1267 mutt_file_copy_stream(fp_in, fp_out);
1268
1269 mutt_file_fclose(&fp_out);
1270 mutt_file_fclose(&fp_in);
1271
1272 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
1273 if ((filter_wait(pid) != 0) || c_wait_key)
1275 rc = 1;
1276 }
1277 decode_cleanup:
1278 mutt_file_fclose(&fp_in);
1279 mutt_file_fclose(&fp_out);
1280 if (unlink_newfile)
1281 mutt_file_unlink(buf_string(newfile));
1282 }
1283 else
1284 {
1285 mutt_error(_("I don't know how to print that"));
1286 rc = 0;
1287 }
1288
1289out:
1290 buf_pool_release(&newfile);
1291 buf_pool_release(&cmd);
1292
1293 return rc;
1294}
1295
1300void mutt_add_temp_attachment(const char *filename)
1301{
1303}
1304
1309{
1310 struct ListNode *np = NULL;
1311
1312 STAILQ_FOREACH(np, &TempAttachmentsList, entries)
1313 {
1314 (void) mutt_file_chmod_add(np->data, S_IWUSR);
1316 }
1317
1319}
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
Handling of email attachments.
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:303
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:407
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
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:292
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
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:649
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:135
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition: curs_lib.c:113
int mutt_any_key_to_continue(const char *s)
Prompt the user to 'press any key' and wait.
Definition: curs_lib.c:184
void mutt_endwin(void)
Shutdown curses.
Definition: curs_lib.c:150
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.
void mutt_parse_part(FILE *fp, struct Body *b)
Parse a MIME part.
Definition: parse.c:1760
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition: parse.c:1330
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1466
bool envlist_set(char ***envp, const char *name, const char *value, bool overwrite)
Set an environment variable.
Definition: envlist.c:87
bool envlist_unset(char ***envp, const char *name)
Unset an environment variable.
Definition: envlist.c:135
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:262
int mutt_file_open(const char *path, uint32_t flags)
Open a file.
Definition: file.c:551
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:636
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:667
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:733
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition: file.c:1412
int mutt_file_symlink(const char *oldpath, const char *newpath)
Create a symlink.
Definition: file.c:290
int mutt_file_chmod_add(const char *path, mode_t mode)
Add permissions to a file.
Definition: file.c:1135
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:196
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:218
pid_t filter_create_fd(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err, int fdin, int fdout, int fderr, char **envlist)
Run a command on a pipe (optionally connect stdin/stdout)
Definition: filter.c:61
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err, char **envlist)
Set up filter program.
Definition: filter.c:207
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:54
struct ListHead MimeLookupList
List of mime types that that shouldn't use the mailcap entry.
Definition: globals.c:52
char ** EnvList
Private copy of the environment variables.
Definition: globals.c:83
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_message(...)
Definition: logging2.h:91
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
#define mutt_perror(...)
Definition: logging2.h:93
Convenience wrapper for the gui headers.
bool mutt_can_decode(struct Body *b)
Will decoding the attachment produce any output.
Definition: handler.c:1858
int mutt_body_handler(struct Body *b, struct State *state)
Handler for the Body of an email.
Definition: handler.c:1624
void mutt_decode_attachment(struct Body *b, struct State *state)
Decode an email's attachment.
Definition: handler.c:1898
Decide how to display email content.
IMAP network mailbox.
int imap_wait_keep_alive(pid_t pid)
Wait for a process to change state.
Definition: util.c:977
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: logging2.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:90
@ 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
void mailcap_entry_free(struct MailcapEntry **ptr)
Deallocate an struct MailcapEntry.
Definition: mailcap.c:451
struct MailcapEntry * mailcap_entry_new(void)
Allocate memory for a new rfc1524 entry.
Definition: mailcap.c:442
int mailcap_expand_command(struct Body *b, 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:549
bool mailcap_lookup(struct Body *b, char *type, size_t typelen, struct MailcapEntry *entry, enum MailcapLookup opt)
Find given type in the list of mailcap files.
Definition: mailcap.c:480
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:45
@ 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:810
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:251
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
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:653
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:525
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:327
bool mutt_edit_attachment(struct Body *b)
Edit an attachment.
Definition: mutt_attach.c:261
void mutt_check_lookup_list(struct Body *b, char *type, size_t len)
Update the mime type.
Definition: mutt_attach.c:339
int mutt_get_tmp_attachment(struct Body *b)
Get a temporary copy of an attachment.
Definition: mutt_attach.c:68
int mutt_pipe_attachment(FILE *fp, struct Body *b, const char *path, const char *outfile)
Pipe an attachment to a command.
Definition: mutt_attach.c:738
int mutt_compose_attachment(struct Body *b)
Create an attachment.
Definition: mutt_attach.c:116
void mutt_temp_attachments_cleanup(void)
Delete all temporary attachments.
Definition: mutt_attach.c:1308
static int wait_interactive_filter(pid_t pid)
Wait after an interactive filter.
Definition: mutt_attach.c:386
int mutt_save_attachment(FILE *fp, struct Body *b, const char *path, enum SaveAttach opt, struct Email *e)
Save an attachment.
Definition: mutt_attach.c:906
int mutt_decode_save_attachment(FILE *fp, struct Body *b, const char *path, StateFlags flags, enum SaveAttach opt)
Decode, then save an attachment.
Definition: mutt_attach.c:1040
int mutt_view_attachment(FILE *fp, struct Body *b, enum ViewAttachMode mode, struct Email *e, struct AttachCtx *actx, struct MuttWindow *win)
View an attachment.
Definition: mutt_attach.c:415
int mutt_print_attachment(FILE *fp, struct Body *b)
Print out an attachment.
Definition: mutt_attach.c:1140
void mutt_add_temp_attachment(const char *filename)
Add file to list of temporary attachments.
Definition: mutt_attach.c:1300
static FILE * save_attachment_open(const char *path, enum SaveAttach opt)
Open a file to write an attachment to.
Definition: mutt_attach.c:886
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 *b)
Does this type need a mailcap entry do display.
Definition: muttlib.c:416
void mutt_adv_mktemp(struct Buffer *buf)
Create a temporary file.
Definition: muttlib.c:85
Some miscellaneous functions.
int mx_msg_close(struct Mailbox *m, struct Message **ptr)
Close a message.
Definition: mx.c:1185
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:284
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1034
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition: mx.c:1164
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1652
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition: mx.c:593
API for mailboxes.
#define MUTT_ADD_FROM
add a From_ line
Definition: mx.h:40
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition: mx.h:39
#define MUTT_APPEND
Open mailbox for appending messages.
Definition: mxapi.h:42
#define MUTT_QUIET
Do not print any messages.
Definition: mxapi.h:44
API for encryption/signing of emails.
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:79
#define WithCrypto
Definition: lib.h:117
const char * pager_get_pager(struct ConfigSubset *sub)
Get the value of $pager.
Definition: config.c:103
GUI display a file/email/help in a viewport with paging.
#define MUTT_PAGER_NO_FLAGS
No flags are set.
Definition: lib.h:59
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition: lib.h:72
@ 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:138
#define MUTT_PAGER_MESSAGE
Definition: lib.h:76
#define MUTT_PAGER_ATTACHMENT
Attachments may exist.
Definition: lib.h:71
void mutt_param_free(struct ParameterList *pl)
Free a ParameterList.
Definition: parameter.c:61
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
Prototypes for many functions.
int mutt_system(const char *cmd)
Run an external command.
Definition: system.c:50
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:39
Ask the user a question.
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:330
#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.
enum ContentType mutt_lookup_mime_type(struct Body *b, const char *path)
Find the MIME type for an attachment.
Definition: sendlib.c:74
void mutt_stamp_attachment(struct Body *b)
Timestamp an Attachment.
Definition: sendlib.c:408
void mutt_sig_unblock_system(bool restore)
Restore previously blocked signals.
Definition: signal.c:220
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:112
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: message.h:34
FILE * fp
pointer to the message data
Definition: message.h:35
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
Data to be displayed by PagerView.
Definition: lib.h:160
const char * fname
Name of the file to read.
Definition: lib.h:164
FILE * fp
Source stream.
Definition: lib.h:162
struct Body * body
Current attachment.
Definition: lib.h:161
struct AttachCtx * actx
Attachment information.
Definition: lib.h:163
Paged view into some data.
Definition: lib.h:171
struct PagerData * pdata
Data that pager displays. NOTNULL.
Definition: lib.h:172
enum PagerMode mode
Pager mode.
Definition: lib.h:173
PagerFlags flags
Additional settings to tweak pager's function.
Definition: lib.h:174
const char * banner
Title to display in status bar.
Definition: lib.h:175
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 buf_mktemp(buf)
Definition: tmp.h:33