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