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