NeoMutt  2025-12-11-980-ge38c27
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
mutt_attach.c
Go to the documentation of this file.
1
26
32
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.h"
49#include "mutt_attach.h"
50#include "lib.h"
51#include "imap/lib.h"
52#include "ncrypt/lib.h"
53#include "pager/lib.h"
54#include "question/lib.h"
55#include "send/lib.h"
56#include "attach.h"
57#include "cid.h"
58#include "module_data.h"
59#include "muttlib.h"
60#include "mx.h"
61
69{
70 char type[256] = { 0 };
71
72 if (b->unlink)
73 return 0;
74
75 struct Buffer *tempfile = buf_pool_get();
76 struct MailcapEntry *entry = mailcap_entry_new();
77 snprintf(type, sizeof(type), "%s/%s", BODY_TYPE(b), b->subtype);
78 mailcap_lookup(b, type, sizeof(type), entry, MUTT_MC_NONE);
79 mailcap_expand_filename(entry->nametemplate, b->filename, tempfile);
80
81 mailcap_entry_free(&entry);
82
83 FILE *fp_in = NULL;
84 FILE *fp_out = NULL;
85 if ((fp_in = mutt_file_fopen(b->filename, "r")) &&
86 (fp_out = mutt_file_fopen(buf_string(tempfile), "w")))
87 {
88 mutt_file_copy_stream(fp_in, fp_out);
89 mutt_str_replace(&b->filename, buf_string(tempfile));
90 b->unlink = true;
91
92 struct stat st = { 0 };
93 if ((fstat(fileno(fp_in), &st) == 0) && (b->stamp >= st.st_mtime))
94 {
96 }
97 }
98 else
99 {
100 mutt_perror("%s", fp_in ? buf_string(tempfile) : b->filename);
101 }
102
103 mutt_file_fclose(&fp_in);
104 mutt_file_fclose(&fp_out);
105
106 buf_pool_release(&tempfile);
107
108 return b->unlink ? 0 : -1;
109}
110
118{
119 char type[256] = { 0 };
120 struct MailcapEntry *entry = mailcap_entry_new();
121 bool unlink_newfile = false;
122 int rc = 0;
123 struct Buffer *cmd = buf_pool_get();
124 struct Buffer *newfile = buf_pool_get();
125 struct Buffer *tempfile = buf_pool_get();
126
127 snprintf(type, sizeof(type), "%s/%s", BODY_TYPE(b), b->subtype);
128 if (mailcap_lookup(b, type, sizeof(type), entry, MUTT_MC_COMPOSE))
129 {
130 if (entry->composecommand || entry->composetypecommand)
131 {
132 if (entry->composetypecommand)
133 buf_strcpy(cmd, entry->composetypecommand);
134 else
135 buf_strcpy(cmd, entry->composecommand);
136
137 mailcap_expand_filename(entry->nametemplate, b->filename, newfile);
138 mutt_debug(LL_DEBUG1, "oldfile: %s newfile: %s\n", b->filename,
139 buf_string(newfile));
140 if (mutt_file_symlink(b->filename, buf_string(newfile)) == -1)
141 {
142 if (query_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
143 goto bailout;
144 buf_strcpy(newfile, b->filename);
145 }
146 else
147 {
148 unlink_newfile = true;
149 }
150
151 if (mailcap_expand_command(b, buf_string(newfile), type, cmd))
152 {
153 /* For now, editing requires a file, no piping */
154 mutt_error(_("Mailcap compose entry requires %%s"));
155 }
156 else
157 {
158 int r;
159
160 mutt_endwin();
161 r = mutt_system(buf_string(cmd));
162 if (r == -1)
163 mutt_error(_("Error running \"%s\""), buf_string(cmd));
164
165 if ((r != -1) && entry->composetypecommand)
166 {
167 FILE *fp = mutt_file_fopen(b->filename, "r");
168 if (!fp)
169 {
170 mutt_perror(_("Failure to open file to parse headers"));
171 goto bailout;
172 }
173
174 struct Body *b_mime = mutt_read_mime_header(fp, 0);
175 if (b_mime)
176 {
177 if (!TAILQ_EMPTY(&b_mime->parameter))
178 {
180 b->parameter = b_mime->parameter;
181 TAILQ_INIT(&b_mime->parameter);
182 }
183 if (b_mime->description)
184 {
185 FREE(&b->description);
186 b->description = b_mime->description;
187 b_mime->description = NULL;
188 }
189 if (b_mime->form_name)
190 {
191 FREE(&b->form_name);
192 b->form_name = b_mime->form_name;
193 b_mime->form_name = NULL;
194 }
195
196 /* Remove headers by copying out data to another file, then
197 * copying the file back */
198 const LOFF_T offset = b_mime->offset;
199 mutt_body_free(&b_mime);
200 if (!mutt_file_seek(fp, offset, SEEK_SET))
201 {
202 goto bailout;
203 }
204
205 buf_mktemp(tempfile);
206 FILE *fp_tmp = mutt_file_fopen(buf_string(tempfile), "w");
207 if (!fp_tmp)
208 {
209 mutt_perror(_("Failure to open file to strip headers"));
210 mutt_file_fclose(&fp);
211 goto bailout;
212 }
213 mutt_file_copy_stream(fp, fp_tmp);
214 mutt_file_fclose(&fp);
215 mutt_file_fclose(&fp_tmp);
217 if (mutt_file_rename(buf_string(tempfile), b->filename) != 0)
218 {
219 mutt_perror(_("Failure to rename file"));
220 goto bailout;
221 }
222 }
223 }
224 }
225 }
226 }
227 else
228 {
229 mutt_message(_("No mailcap compose entry for %s, creating empty file"), type);
230 rc = 1;
231 goto bailout;
232 }
233
234 rc = 1;
235
236bailout:
237
238 if (unlink_newfile)
239 unlink(buf_string(newfile));
240
241 buf_pool_release(&cmd);
242 buf_pool_release(&newfile);
243 buf_pool_release(&tempfile);
244
245 mailcap_entry_free(&entry);
246 return rc;
247}
248
263{
264 char type[256] = { 0 };
265 struct MailcapEntry *entry = mailcap_entry_new();
266 bool unlink_newfile = false;
267 bool rc = false;
268 struct Buffer *cmd = buf_pool_get();
269 struct Buffer *newfile = buf_pool_get();
270
271 snprintf(type, sizeof(type), "%s/%s", BODY_TYPE(b), b->subtype);
272 if (mailcap_lookup(b, type, sizeof(type), entry, MUTT_MC_EDIT))
273 {
274 if (entry->editcommand)
275 {
276 buf_strcpy(cmd, entry->editcommand);
277 mailcap_expand_filename(entry->nametemplate, b->filename, newfile);
278 mutt_debug(LL_DEBUG1, "oldfile: %s newfile: %s\n", b->filename,
279 buf_string(newfile));
280 if (mutt_file_symlink(b->filename, buf_string(newfile)) == -1)
281 {
282 if (query_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
283 goto bailout;
284 buf_strcpy(newfile, b->filename);
285 }
286 else
287 {
288 unlink_newfile = true;
289 }
290
291 if (mailcap_expand_command(b, buf_string(newfile), type, cmd))
292 {
293 /* For now, editing requires a file, no piping */
294 mutt_error(_("Mailcap Edit entry requires %%s"));
295 goto bailout;
296 }
297 else
298 {
299 mutt_endwin();
300 if (mutt_system(buf_string(cmd)) == -1)
301 {
302 mutt_error(_("Error running \"%s\""), buf_string(cmd));
303 goto bailout;
304 }
305 }
306 }
307 }
308 else if (b->type == TYPE_TEXT)
309 {
310 /* On text, default to editor */
311 const char *const c_editor = cs_subset_string(NeoMutt->sub, "editor");
312 mutt_edit_file(NONULL(c_editor), b->filename);
313 }
314 else
315 {
316 mutt_error(_("No mailcap edit entry for %s"), type);
317 goto bailout;
318 }
319
320 rc = true;
321
322bailout:
323
324 if (unlink_newfile)
325 unlink(buf_string(newfile));
326
327 buf_pool_release(&cmd);
328 buf_pool_release(&newfile);
329
330 mailcap_entry_free(&entry);
331 return rc;
332}
333
340void mutt_check_lookup_list(struct Body *b, char *type, size_t len)
341{
343 ASSERT(mod_data);
344
345 struct ListNode *np = NULL;
346 STAILQ_FOREACH(np, &mod_data->mime_lookup, 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", BODY_TYPE(b), b->subtype);
448
449 char columns[16] = { 0 };
450 snprintf(columns, sizeof(columns), "%d", win->state.cols);
451 envlist_set(&NeoMutt->env, "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_NONE;
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;
539 int fd_pager = -1;
540
541 if (!use_pager)
542 mutt_endwin();
543
544 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
545 if (use_pager || use_pipe)
546 {
547 if (use_pager &&
548 ((fd_pager = mutt_file_open(buf_string(pagerfile),
549 O_CREAT | O_EXCL | O_WRONLY, 0600)) == -1))
550 {
551 mutt_perror("open");
552 goto return_error;
553 }
554 unlink_pagerfile = true;
555
556 if (use_pipe && ((fd_temp = open(buf_string(tempfile), 0)) == -1))
557 {
558 if (fd_pager != -1)
559 close(fd_pager);
560 mutt_perror("open");
561 goto return_error;
562 }
563 unlink_pagerfile = true;
564
565 pid = filter_create_fd(buf_string(cmd), NULL, NULL, NULL, use_pipe ? fd_temp : -1,
566 use_pager ? fd_pager : -1, -1, NeoMutt->env);
567
568 if (pid == -1)
569 {
570 if (fd_pager != -1)
571 close(fd_pager);
572
573 if (fd_temp != -1)
574 close(fd_temp);
575
576 mutt_error(_("Can't create filter"));
577 goto return_error;
578 }
579
580 if (use_pager)
581 {
582 if (b->description)
583 {
584 snprintf(desc, sizeof(desc), _("---Command: %-20.20s Description: %s"),
585 buf_string(cmd), b->description);
586 }
587 else
588 {
589 snprintf(desc, sizeof(desc), _("---Command: %-30.30s Attachment: %s"),
590 buf_string(cmd), type);
591 }
592 filter_wait(pid);
593 }
594 else
595 {
596 if (wait_interactive_filter(pid) || (entry->needsterminal && c_wait_key))
598 }
599
600 if (fd_temp != -1)
601 close(fd_temp);
602 if (fd_pager != -1)
603 close(fd_pager);
604 }
605 else
606 {
607 /* interactive cmd */
608 int rv = mutt_system(buf_string(cmd));
609 if (rv == -1)
610 mutt_debug(LL_DEBUG1, "Error running \"%s\"\n", cmd->data);
611
612 if ((rv != 0) || (entry->needsterminal && c_wait_key))
614 }
615 }
616 else
617 {
618 /* Don't use mailcap; the attachment is viewed in the pager */
619
620 if (mode == MUTT_VA_AS_TEXT)
621 {
622 /* just let me see the raw data */
623 if (fp)
624 {
625 /* Viewing from a received message.
626 *
627 * Don't use mutt_save_attachment() because we want to perform charset
628 * conversion since this will be displayed by the internal pager. */
629 struct State state = { 0 };
630
631 state.fp_out = mutt_file_fopen(buf_string(pagerfile), "w");
632 if (!state.fp_out)
633 {
634 mutt_debug(LL_DEBUG1, "mutt_file_fopen(%s) errno=%d %s\n",
635 buf_string(pagerfile), errno, strerror(errno));
636 mutt_perror("%s", buf_string(pagerfile));
637 goto return_error;
638 }
639 state.fp_in = fp;
640 state.flags = STATE_CHARCONV;
641 mutt_decode_attachment(b, &state);
642 mutt_file_fclose(&state.fp_out);
643 }
644 else
645 {
646 /* in compose mode, just copy the file. we can't use
647 * mutt_decode_attachment() since it assumes the content-encoding has
648 * already been applied */
649 if (mutt_save_attachment(fp, b, buf_string(pagerfile), MUTT_SAVE_NONE, NULL))
650 goto return_error;
651 unlink_pagerfile = true;
652 }
654 }
655 else
656 {
658 const char *const c_pager = pager_get_pager(NeoMutt->sub);
659 if (!c_pager)
661
662 /* Use built-in handler */
664 {
665 goto return_error;
666 }
667 unlink_pagerfile = true;
668 }
669
670 if (b->description)
671 mutt_str_copy(desc, b->description, sizeof(desc));
672 else if (b->filename)
673 snprintf(desc, sizeof(desc), _("---Attachment: %s: %s"), b->filename, type);
674 else
675 snprintf(desc, sizeof(desc), _("---Attachment: %s"), type);
676 }
677
678 /* We only reach this point if there have been no errors */
679
680 if (use_pager)
681 {
682 struct PagerData pdata = { 0 };
683 struct PagerView pview = { &pdata };
684
685 pdata.actx = actx;
686 pdata.body = b;
687 pdata.fname = buf_string(pagerfile);
688 pdata.fp = fp;
689
690 pview.banner = desc;
692 (is_message ? MUTT_PAGER_MESSAGE : MUTT_PAGER_NONE) |
693 ((use_mailcap && entry->xneomuttnowrap) ? MUTT_PAGER_NOWRAP : MUTT_PAGER_NONE);
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 list of files 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(&NeoMutt->env, "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;
743 int rc = 0;
744 bool is_flowed = false;
745 bool unlink_unstuff = false;
746 FILE *fp_filter = NULL;
747 FILE *fp_unstuff = NULL;
748 FILE *fp_in = NULL;
749 struct Buffer *unstuff_tempfile = NULL;
750
751 if (outfile && *outfile)
752 {
753 out = mutt_file_open(outfile, O_CREAT | O_EXCL | O_WRONLY, 0600);
754 if (out < 0)
755 {
756 mutt_perror("open");
757 return 0;
758 }
759 }
760
762 {
763 is_flowed = true;
764 unstuff_tempfile = buf_pool_get();
765 buf_mktemp(unstuff_tempfile);
766 }
767
768 mutt_endwin();
769
770 if (outfile && *outfile)
771 pid = filter_create_fd(path, &fp_filter, NULL, NULL, -1, out, -1, NeoMutt->env);
772 else
773 pid = filter_create(path, &fp_filter, NULL, NULL, NeoMutt->env);
774 if (pid < 0)
775 {
776 mutt_perror(_("Can't create filter"));
777 goto bail;
778 }
779
780 /* recv case */
781 if (fp)
782 {
783 struct State state = { 0 };
784
785 /* perform charset conversion on text attachments when piping */
786 state.flags = STATE_CHARCONV;
787
788 if (is_flowed)
789 {
790 fp_unstuff = mutt_file_fopen(buf_string(unstuff_tempfile), "w");
791 if (!fp_unstuff)
792 {
793 mutt_perror("mutt_file_fopen");
794 goto bail;
795 }
796 unlink_unstuff = true;
797
798 state.fp_in = fp;
799 state.fp_out = fp_unstuff;
800 mutt_decode_attachment(b, &state);
801 mutt_file_fclose(&fp_unstuff);
802
804
805 fp_unstuff = mutt_file_fopen(buf_string(unstuff_tempfile), "r");
806 if (!fp_unstuff)
807 {
808 mutt_perror("mutt_file_fopen");
809 goto bail;
810 }
811 mutt_file_copy_stream(fp_unstuff, fp_filter);
812 mutt_file_fclose(&fp_unstuff);
813 }
814 else
815 {
816 state.fp_in = fp;
817 state.fp_out = fp_filter;
818 mutt_decode_attachment(b, &state);
819 }
820 }
821 else
822 {
823 /* send case */
824 const char *infile = NULL;
825
826 if (is_flowed)
827 {
828 if (mutt_save_attachment(fp, b, buf_string(unstuff_tempfile),
829 MUTT_SAVE_NONE, NULL) == -1)
830 {
831 goto bail;
832 }
833 unlink_unstuff = true;
835 infile = buf_string(unstuff_tempfile);
836 }
837 else
838 {
839 infile = b->filename;
840 }
841
842 fp_in = mutt_file_fopen(infile, "r");
843 if (!fp_in)
844 {
845 mutt_perror("fopen");
846 goto bail;
847 }
848
849 mutt_file_copy_stream(fp_in, fp_filter);
851 }
852
853 mutt_file_fclose(&fp_filter);
854 rc = 1;
855
856bail:
857 if (outfile && *outfile)
858 {
859 close(out);
860 if (rc == 0)
861 unlink(outfile);
862 else if (is_flowed)
864 }
865
866 mutt_file_fclose(&fp_unstuff);
867 mutt_file_fclose(&fp_filter);
869
870 if (unlink_unstuff)
871 mutt_file_unlink(buf_string(unstuff_tempfile));
872 buf_pool_release(&unstuff_tempfile);
873
874 /* check for error exit from child process */
875 if ((pid > 0) && (filter_wait(pid) != 0))
876 rc = 0;
877
878 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
879 if ((rc == 0) || c_wait_key)
881 return rc;
882}
883
890static FILE *save_attachment_open(const char *path, enum SaveAttach opt)
891{
892 if (opt == MUTT_SAVE_APPEND)
893 return mutt_file_fopen_masked(path, "a");
894 else
895 return mutt_file_fopen_masked(path, "w");
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;
925 CopyHeaderFlags chflags = CH_NONE;
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, is_from(buf, NULL, 0, NULL) ? MUTT_MSG_NONE : MUTT_ADD_FROM);
943 if (!msg)
944 {
945 mx_mbox_close(m_att);
946 return -1;
947 }
948 if ((m_att->type == MUTT_MBOX) || (m_att->type == MUTT_MMDF))
949 chflags = CH_FROM | CH_UPDATE_LEN;
950 chflags |= ((m_att->type == MUTT_MAILDIR) ? CH_NOSTATUS : CH_UPDATE);
951 if ((mutt_copy_message_fp(msg->fp, fp, e_new, MUTT_CM_NONE, chflags, 0) == 0) &&
952 (mx_msg_commit(m_att, msg) == 0))
953 {
954 rc = 0;
955 }
956 else
957 {
958 rc = -1;
959 }
960
961 mx_msg_close(m_att, &msg);
962 mx_mbox_close(m_att);
963 return rc;
964 }
965 else
966 {
967 /* In recv mode, extract from folder and decode */
968
969 struct State state = { 0 };
970
971 state.fp_out = save_attachment_open(path, opt);
972 if (!state.fp_out)
973 {
974 mutt_perror("fopen");
975 return -1;
976 }
977 if (!mutt_file_seek((state.fp_in = fp), b->offset, SEEK_SET))
978 {
979 mutt_file_fclose(&state.fp_out);
980 return -1;
981 }
982 mutt_decode_attachment(b, &state);
983
984 if (mutt_file_fsync_close(&state.fp_out) != 0)
985 {
986 mutt_perror("fclose");
987 return -1;
988 }
989 }
990 }
991 else
992 {
993 if (!b->filename)
994 return -1;
995
996 /* In send mode, just copy file */
997
998 FILE *fp_old = mutt_file_fopen(b->filename, "r");
999 if (!fp_old)
1000 {
1001 mutt_perror("fopen");
1002 return -1;
1003 }
1004
1005 FILE *fp_new = save_attachment_open(path, opt);
1006 if (!fp_new)
1007 {
1008 mutt_perror("fopen");
1009 mutt_file_fclose(&fp_old);
1010 return -1;
1011 }
1012
1013 if (mutt_file_copy_stream(fp_old, fp_new) == -1)
1014 {
1015 mutt_error(_("Write fault"));
1016 mutt_file_fclose(&fp_old);
1017 mutt_file_fclose(&fp_new);
1018 return -1;
1019 }
1020 mutt_file_fclose(&fp_old);
1021 if (mutt_file_fsync_close(&fp_new) != 0)
1022 {
1023 mutt_error(_("Write fault"));
1024 return -1;
1025 }
1026 }
1027
1028 return 0;
1029}
1030
1041int mutt_decode_save_attachment(FILE *fp, struct Body *b, const char *path,
1042 StateFlags flags, enum SaveAttach opt)
1043{
1044 struct State state = { 0 };
1045 unsigned int saved_encoding = 0;
1046 struct Body *saved_parts = NULL;
1047 struct Email *e_saved = NULL;
1048 int rc = 0;
1049
1050 state.flags = flags;
1051
1052 if (opt == MUTT_SAVE_APPEND)
1053 state.fp_out = mutt_file_fopen_masked(path, "a");
1054 else
1055 state.fp_out = mutt_file_fopen_masked(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;
1144 FILE *fp_out = NULL;
1145 bool unlink_newfile = false;
1146 struct Buffer *newfile = buf_pool_get();
1147 struct Buffer *cmd = buf_pool_get();
1148
1149 int rc = 0;
1150
1151 snprintf(type, sizeof(type), "%s/%s", BODY_TYPE(b), b->subtype);
1152
1153 if (mailcap_lookup(b, type, sizeof(type), NULL, MUTT_MC_PRINT))
1154 {
1155 mutt_debug(LL_DEBUG2, "Using mailcap\n");
1156
1157 struct MailcapEntry *entry = mailcap_entry_new();
1158 mailcap_lookup(b, type, sizeof(type), entry, MUTT_MC_PRINT);
1159
1160 char *sanitized_fname = mutt_str_dup(b->filename);
1161 /* In send mode (!fp), we allow slashes because those are part of
1162 * the tempfile. The path will be removed in expand_filename */
1163 mutt_file_sanitize_filename(sanitized_fname, fp ? true : false);
1164 mailcap_expand_filename(entry->nametemplate, sanitized_fname, newfile);
1165 FREE(&sanitized_fname);
1166
1167 if (mutt_save_attachment(fp, b, buf_string(newfile), MUTT_SAVE_NONE, NULL) == -1)
1168 {
1169 goto mailcap_cleanup;
1170 }
1171 unlink_newfile = 1;
1172
1174
1175 buf_strcpy(cmd, entry->printcommand);
1176
1177 bool piped = mailcap_expand_command(b, buf_string(newfile), type, cmd);
1178
1179 mutt_endwin();
1180
1181 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
1182 /* interactive program */
1183 if (piped)
1184 {
1185 fp_in = mutt_file_fopen(buf_string(newfile), "r");
1186 if (!fp_in)
1187 {
1188 mutt_perror("fopen");
1189 mailcap_entry_free(&entry);
1190 goto mailcap_cleanup;
1191 }
1192
1193 pid = filter_create(buf_string(cmd), &fp_out, NULL, NULL, NeoMutt->env);
1194 if (pid < 0)
1195 {
1196 mutt_perror(_("Can't create filter"));
1197 mailcap_entry_free(&entry);
1198 mutt_file_fclose(&fp_in);
1199 goto mailcap_cleanup;
1200 }
1201 mutt_file_copy_stream(fp_in, fp_out);
1202 mutt_file_fclose(&fp_out);
1203 mutt_file_fclose(&fp_in);
1204 if (filter_wait(pid) || c_wait_key)
1206 }
1207 else
1208 {
1209 int rc2 = mutt_system(buf_string(cmd));
1210 if (rc2 == -1)
1211 mutt_debug(LL_DEBUG1, "Error running \"%s\"\n", cmd->data);
1212
1213 if ((rc2 != 0) || c_wait_key)
1215 }
1216
1217 rc = 1;
1218
1219 mailcap_cleanup:
1220 if (unlink_newfile)
1221 mutt_file_unlink(buf_string(newfile));
1222
1223 mailcap_entry_free(&entry);
1224 goto out;
1225 }
1226
1227 const char *const c_print_command = cs_subset_string(NeoMutt->sub, "print_command");
1228 if (mutt_istr_equal("text/plain", type) || mutt_istr_equal("application/postscript", type))
1229 {
1230 rc = (mutt_pipe_attachment(fp, b, NONULL(c_print_command), NULL));
1231 goto out;
1232 }
1233 else if (mutt_can_decode(b))
1234 {
1235 /* decode and print */
1236
1237 fp_in = NULL;
1238 fp_out = NULL;
1239
1240 buf_mktemp(newfile);
1242 MUTT_SAVE_NONE) == 0)
1243 {
1244 unlink_newfile = true;
1245 mutt_debug(LL_DEBUG2, "successfully decoded %s type attachment to %s\n",
1246 type, buf_string(newfile));
1247
1248 fp_in = mutt_file_fopen(buf_string(newfile), "r");
1249 if (!fp_in)
1250 {
1251 mutt_perror("fopen");
1252 goto decode_cleanup;
1253 }
1254
1255 mutt_debug(LL_DEBUG2, "successfully opened %s read-only\n", buf_string(newfile));
1256
1257 mutt_endwin();
1258 pid = filter_create(NONULL(c_print_command), &fp_out, NULL, NULL, NeoMutt->env);
1259 if (pid < 0)
1260 {
1261 mutt_perror(_("Can't create filter"));
1262 goto decode_cleanup;
1263 }
1264
1265 mutt_debug(LL_DEBUG2, "Filter created\n");
1266
1267 mutt_file_copy_stream(fp_in, fp_out);
1268
1269 mutt_file_fclose(&fp_out);
1270 mutt_file_fclose(&fp_in);
1271
1272 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
1273 if ((filter_wait(pid) != 0) || c_wait_key)
1275 rc = 1;
1276 }
1277 decode_cleanup:
1278 mutt_file_fclose(&fp_in);
1279 mutt_file_fclose(&fp_out);
1280 if (unlink_newfile)
1281 mutt_file_unlink(buf_string(newfile));
1282 }
1283 else
1284 {
1285 mutt_error(_("I don't know how to print that"));
1286 rc = 0;
1287 }
1288
1289out:
1290 buf_pool_release(&newfile);
1291 buf_pool_release(&cmd);
1292
1293 return rc;
1294}
1295
1300void mutt_add_temp_attachment(const char *filename)
1301{
1303 ASSERT(mod_data);
1304
1306}
1307
1312void mutt_temp_attachments_cleanup(struct ListHead *list)
1313{
1314 if (!list)
1315 return;
1316
1317 struct ListNode *np = NULL;
1318
1319 STAILQ_FOREACH(np, list, entries)
1320 {
1321 (void) mutt_file_chmod_add(np->data, S_IWUSR);
1323 }
1324
1325 mutt_list_free(list);
1326}
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
GUI display the mailboxes in a side panel.
Attach private Module data.
Handling of email attachments.
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:298
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:401
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:172
void cid_to_filename(struct Buffer *filename, const struct CidMapList *cid_map_list)
Replace Content-IDs with filenames.
Definition cid.c:182
void cid_map_list_clear(struct CidMapList *cid_map_list)
Empty a CidMapList.
Definition cid.c:80
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_email.c:663
@ MUTT_CM_NONE
No flags are set.
Definition copy_email.h:41
uint32_t CopyHeaderFlags
Definition copy_email.h:89
@ CH_UPDATE
Update the status and x-status fields?
Definition copy_email.h:66
@ CH_UPDATE_LEN
Update Lines: and Content-Length:
Definition copy_email.h:76
@ CH_FROM
Retain the "From " message separator?
Definition copy_email.h:70
@ CH_NOSTATUS
Suppress the status and x-status fields.
Definition copy_email.h:72
@ CH_NONE
No flags are set.
Definition copy_email.h:65
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:45
@ MUTT_MBOX
'mbox' Mailbox type
Definition mailbox.h:44
@ MUTT_MAILDIR
'Maildir' Mailbox type
Definition mailbox.h:47
bool crypt_valid_passphrase(SecurityFlags flags)
Check that we have a usable passphrase, ask if not.
Definition crypt.c:131
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition curs_lib.c:117
int mutt_any_key_to_continue(const char *s)
Prompt the user to 'press any key' and wait.
Definition curs_lib.c:174
void mutt_endwin(void)
Shutdown curses.
Definition curs_lib.c:152
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:1949
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition parse.c:1484
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition parse.c:1620
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:140
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition file.c:224
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition file.c:582
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition file.c:648
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition file.c:1258
int mutt_file_symlink(const char *oldpath, const char *newpath)
Create a symlink.
Definition file.c:254
int mutt_file_chmod_add(const char *path, mode_t mode)
Add permissions to a file.
Definition file.c:1002
int mutt_file_open(const char *path, uint32_t flags, mode_t mode)
Open a file.
Definition file.c:516
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition file.c:156
int mutt_file_fsync_close(FILE **fp)
Flush the data, before closing a file (and NULL the pointer)
Definition file.c:128
#define mutt_file_fclose(FP)
Definition file.h:144
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
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
#define mutt_error(...)
Definition logging2.h:94
#define mutt_message(...)
Definition logging2.h:93
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
#define mutt_perror(...)
Definition logging2.h:95
Convenience wrapper for the gui headers.
bool mutt_can_decode(struct Body *b)
Will decoding the attachment produce any output.
Definition handler.c:1903
int mutt_body_handler(struct Body *b, struct State *state)
Handler for the Body of an email.
Definition handler.c:1668
void mutt_decode_attachment(const struct Body *b, struct State *state)
Decode an email's attachment.
Definition handler.c:1943
IMAP network mailbox.
int imap_wait_keep_alive(pid_t pid)
Wait for a process to change state.
Definition util.c:1034
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:46
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
void mailcap_entry_free(struct MailcapEntry **ptr)
Deallocate an struct MailcapEntry.
Definition mailcap.c:455
struct MailcapEntry * mailcap_entry_new(void)
Allocate memory for a new rfc1524 entry.
Definition mailcap.c:446
int mailcap_expand_command(struct Body *b, const char *filename, const char *type, struct Buffer *command)
Expand expandos in a command.
Definition mailcap.c:70
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:568
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:484
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_NONE
No flags set.
Definition mailcap.h:57
@ MUTT_MC_AUTOVIEW
Mailcap autoview field.
Definition mailcap.h:61
@ MUTT_MC_COMPOSE
Mailcap compose field.
Definition mailcap.h:59
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
@ 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 BODY_TYPE(body)
Get the type name of a body part.
Definition mime.h:93
#define is_multipart(body)
Check if a body part is multipart or a message container.
Definition mime.h:85
@ MODULE_ID_ATTACH
ModuleAttach, Attachments
Definition module_api.h:49
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition filter.c:231
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:220
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
uint16_t StateFlags
Definition state.h:48
@ STATE_CHARCONV
Do character set conversions.
Definition state.h:41
@ STATE_PRINTING
Are we printing? - STATE_DISPLAY "light".
Definition state.h:42
@ STATE_PAGER
Output will be displayed in the Pager.
Definition state.h:46
@ STATE_DISPLAY_ATTACH
We are displaying an attachment.
Definition state.h:45
@ STATE_DISPLAY
Output is displayed to the user.
Definition state.h:37
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:678
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:666
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
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:587
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:457
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
Many unsorted constants and some structs.
int mutt_system(const char *cmd)
Run an external command.
Definition system.c:51
bool mutt_edit_attachment(struct Body *b)
Edit an attachment.
void mutt_check_lookup_list(struct Body *b, char *type, size_t len)
Update the mime type.
int mutt_get_tmp_attachment(struct Body *b)
Get a temporary copy of an attachment.
Definition mutt_attach.c:68
void mutt_temp_attachments_cleanup(struct ListHead *list)
Delete all temporary attachments.
int mutt_pipe_attachment(FILE *fp, struct Body *b, const char *path, const char *outfile)
Pipe an attachment to a command.
int mutt_compose_attachment(struct Body *b)
Create an attachment.
static int wait_interactive_filter(pid_t pid)
Wait after an interactive filter.
int mutt_save_attachment(FILE *fp, struct Body *b, const char *path, enum SaveAttach opt, struct Email *e)
Save an attachment.
int mutt_decode_save_attachment(FILE *fp, struct Body *b, const char *path, StateFlags flags, enum SaveAttach opt)
Decode, then save an attachment.
int mutt_view_attachment(FILE *fp, struct Body *b, enum ViewAttachMode mode, struct Email *e, struct AttachCtx *actx, struct MuttWindow *win)
View an attachment.
int mutt_print_attachment(FILE *fp, struct Body *b)
Print out an attachment.
void mutt_add_temp_attachment(const char *filename)
Add file to list of temporary attachments.
static FILE * save_attachment_open(const char *path, enum SaveAttach opt)
Open a file to write an attachment to.
Handling of email attachments.
SaveAttach
Options for saving attachments.
Definition mutt_attach.h:58
@ MUTT_SAVE_APPEND
Append to existing file.
Definition mutt_attach.h:60
@ MUTT_SAVE_NONE
Overwrite existing file (the default)
Definition mutt_attach.h:59
ViewAttachMode
Options for mutt_view_attachment()
Definition mutt_attach.h:44
@ MUTT_VA_MAILCAP
Force viewing using mailcap entry.
Definition mutt_attach.h:46
@ MUTT_VA_REGULAR
View using default method.
Definition mutt_attach.h:45
@ MUTT_VA_PAGER
View attachment in pager using copiousoutput mailcap.
Definition mutt_attach.h:48
@ MUTT_VA_AS_TEXT
Force viewing as text.
Definition mutt_attach.h:47
bool mutt_needs_mailcap(struct Body *b)
Does this type need a mailcap entry do display.
Definition muttlib.c:368
Some miscellaneous functions.
#define mutt_adv_mktemp(buf)
Definition muttlib.h:39
int mx_msg_close(struct Mailbox *m, struct Message **ptr)
Close a message.
Definition mx.c:1185
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition mx.c:285
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition mx.c:1044
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition mx.c:1164
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition mx.c:1650
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition mx.c:595
API for mailboxes.
@ MUTT_ADD_FROM
add a From_ line
Definition mx.h:43
@ MUTT_MSG_NONE
No flags are set.
Definition mx.h:42
@ MUTT_QUIET
Do not print any messages.
Definition mxapi.h:46
@ MUTT_APPEND
Open mailbox for appending messages.
Definition mxapi.h:44
API for encryption/signing of emails.
@ SEC_ENCRYPT
Email is encrypted.
Definition lib.h:92
#define WithCrypto
Definition lib.h:132
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:666
#define mutt_file_fopen_masked(PATH, MODE)
Open a file with proper permissions, tracked for debugging.
Definition neomutt.h:92
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_NOWRAP
Format for term width, ignore $wrap.
Definition lib.h:74
#define MUTT_PAGER_NONE
No flags are set.
Definition lib.h:63
@ 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:140
#define MUTT_PAGER_MESSAGE
Definition lib.h:78
#define MUTT_PAGER_ATTACHMENT
Attachments may exist.
Definition lib.h:73
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:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
@ 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:329
#define STAILQ_HEAD_INITIALIZER(head)
Definition queue.h:324
#define TAILQ_INIT(head)
Definition queue.h:822
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
#define TAILQ_EMPTY(head)
Definition queue.h:778
void mutt_rfc3676_space_stuff_attachment(struct Body *b, const char *filename)
Stuff attachments.
Definition rfc3676.c:545
void mutt_rfc3676_space_unstuff_attachment(struct Body *b, const char *filename)
Unstuff attachments.
Definition rfc3676.c:524
bool mutt_rfc3676_is_format_flowed(struct Body *b)
Is the Email "format-flowed"?
Definition rfc3676.c:400
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:412
#define ASSERT(COND)
Definition signal2.h:59
void mutt_sig_unblock_system(bool restore)
Restore previously blocked signals.
Definition signal.c:284
#define NONULL(x)
Definition string2.h:44
A set of attachments.
Definition attach.h:65
struct Body ** body_idx
Extra struct Body* used for decryption.
Definition attach.h:80
Attach private Module data.
Definition module_data.h:32
struct ListHead temp_attachments
List of temporary files for displaying attachments.
Definition module_data.h:43
struct ListHead mime_lookup
List of mime types that that shouldn't use the mailcap entry.
Definition module_data.h:42
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:81
enum MailboxType type
Mailbox type.
Definition mailbox.h:104
A mailcap entry.
Definition mailcap.h:37
char * composecommand
Compose command.
Definition mailcap.h:40
bool needsterminal
endwin() and system
Definition mailcap.h:46
char * nametemplate
Filename template.
Definition mailcap.h:44
char * printcommand
Print command.
Definition mailcap.h:43
char * composetypecommand
Compose type command.
Definition mailcap.h:41
char * editcommand
Edit command.
Definition mailcap.h:42
char * command
Command to run.
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.
Container for Accounts, Notifications.
Definition neomutt.h:41
char ** env
Private copy of the environment variables.
Definition neomutt.h:57
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
Data to be displayed by PagerView.
Definition lib.h:162
const char * fname
Name of the file to read.
Definition lib.h:166
FILE * fp
Source stream.
Definition lib.h:164
struct Body * body
Current attachment.
Definition lib.h:163
struct AttachCtx * actx
Attachment information.
Definition lib.h:165
Paged view into some data.
Definition lib.h:173
struct PagerData * pdata
Data that pager displays. NOTNULL.
Definition lib.h:174
enum PagerMode mode
Pager mode.
Definition lib.h:175
PagerFlags flags
Additional settings to tweak pager's function.
Definition lib.h:176
const char * banner
Title to display in status bar.
Definition lib.h:177
Keep track when processing files.
Definition state.h:54
StateFlags flags
Flags, e.g. STATE_DISPLAY.
Definition state.h:58
FILE * fp_out
File to write to.
Definition state.h:56
FILE * fp_in
File to read from.
Definition state.h:55
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition mutt_window.h:60
#define buf_mktemp(buf)
Definition tmp.h:33