NeoMutt  2025-12-11-435-g4ac674
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_NO_FLAGS);
79 mailcap_expand_filename(entry->nametemplate, b->filename, tempfile);
80
81 mailcap_entry_free(&entry);
82
83 FILE *fp_in = NULL, *fp_out = NULL;
84 if ((fp_in = mutt_file_fopen(b->filename, "r")) &&
85 (fp_out = mutt_file_fopen(buf_string(tempfile), "w")))
86 {
87 mutt_file_copy_stream(fp_in, fp_out);
88 mutt_str_replace(&b->filename, buf_string(tempfile));
89 b->unlink = true;
90
91 struct stat st = { 0 };
92 if ((fstat(fileno(fp_in), &st) == 0) && (b->stamp >= st.st_mtime))
93 {
95 }
96 }
97 else
98 {
99 mutt_perror("%s", fp_in ? buf_string(tempfile) : b->filename);
100 }
101
102 mutt_file_fclose(&fp_in);
103 mutt_file_fclose(&fp_out);
104
105 buf_pool_release(&tempfile);
106
107 return b->unlink ? 0 : -1;
108}
109
117{
118 char type[256] = { 0 };
119 struct MailcapEntry *entry = mailcap_entry_new();
120 bool unlink_newfile = false;
121 int rc = 0;
122 struct Buffer *cmd = buf_pool_get();
123 struct Buffer *newfile = buf_pool_get();
124 struct Buffer *tempfile = buf_pool_get();
125
126 snprintf(type, sizeof(type), "%s/%s", BODY_TYPE(b), b->subtype);
127 if (mailcap_lookup(b, type, sizeof(type), entry, MUTT_MC_COMPOSE))
128 {
129 if (entry->composecommand || entry->composetypecommand)
130 {
131 if (entry->composetypecommand)
132 buf_strcpy(cmd, entry->composetypecommand);
133 else
134 buf_strcpy(cmd, entry->composecommand);
135
136 mailcap_expand_filename(entry->nametemplate, b->filename, newfile);
137 mutt_debug(LL_DEBUG1, "oldfile: %s newfile: %s\n", b->filename,
138 buf_string(newfile));
139 if (mutt_file_symlink(b->filename, buf_string(newfile)) == -1)
140 {
141 if (query_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
142 goto bailout;
143 buf_strcpy(newfile, b->filename);
144 }
145 else
146 {
147 unlink_newfile = true;
148 }
149
150 if (mailcap_expand_command(b, buf_string(newfile), type, cmd))
151 {
152 /* For now, editing requires a file, no piping */
153 mutt_error(_("Mailcap compose entry requires %%s"));
154 }
155 else
156 {
157 int r;
158
159 mutt_endwin();
160 r = mutt_system(buf_string(cmd));
161 if (r == -1)
162 mutt_error(_("Error running \"%s\""), buf_string(cmd));
163
164 if ((r != -1) && entry->composetypecommand)
165 {
166 FILE *fp = mutt_file_fopen(b->filename, "r");
167 if (!fp)
168 {
169 mutt_perror(_("Failure to open file to parse headers"));
170 goto bailout;
171 }
172
173 struct Body *b_mime = mutt_read_mime_header(fp, 0);
174 if (b_mime)
175 {
176 if (!TAILQ_EMPTY(&b_mime->parameter))
177 {
179 b->parameter = b_mime->parameter;
180 TAILQ_INIT(&b_mime->parameter);
181 }
182 if (b_mime->description)
183 {
184 FREE(&b->description);
185 b->description = b_mime->description;
186 b_mime->description = NULL;
187 }
188 if (b_mime->form_name)
189 {
190 FREE(&b->form_name);
191 b->form_name = b_mime->form_name;
192 b_mime->form_name = NULL;
193 }
194
195 /* Remove headers by copying out data to another file, then
196 * copying the file back */
197 const LOFF_T offset = b_mime->offset;
198 mutt_body_free(&b_mime);
199 if (!mutt_file_seek(fp, offset, SEEK_SET))
200 {
201 goto bailout;
202 }
203
204 buf_mktemp(tempfile);
205 FILE *fp_tmp = mutt_file_fopen(buf_string(tempfile), "w");
206 if (!fp_tmp)
207 {
208 mutt_perror(_("Failure to open file to strip headers"));
209 mutt_file_fclose(&fp);
210 goto bailout;
211 }
212 mutt_file_copy_stream(fp, fp_tmp);
213 mutt_file_fclose(&fp);
214 mutt_file_fclose(&fp_tmp);
216 if (mutt_file_rename(buf_string(tempfile), b->filename) != 0)
217 {
218 mutt_perror(_("Failure to rename file"));
219 goto bailout;
220 }
221 }
222 }
223 }
224 }
225 }
226 else
227 {
228 mutt_message(_("No mailcap compose entry for %s, creating empty file"), type);
229 rc = 1;
230 goto bailout;
231 }
232
233 rc = 1;
234
235bailout:
236
237 if (unlink_newfile)
238 unlink(buf_string(newfile));
239
240 buf_pool_release(&cmd);
241 buf_pool_release(&newfile);
242 buf_pool_release(&tempfile);
243
244 mailcap_entry_free(&entry);
245 return rc;
246}
247
262{
263 char type[256] = { 0 };
264 struct MailcapEntry *entry = mailcap_entry_new();
265 bool unlink_newfile = false;
266 bool rc = false;
267 struct Buffer *cmd = buf_pool_get();
268 struct Buffer *newfile = buf_pool_get();
269
270 snprintf(type, sizeof(type), "%s/%s", BODY_TYPE(b), b->subtype);
271 if (mailcap_lookup(b, type, sizeof(type), entry, MUTT_MC_EDIT))
272 {
273 if (entry->editcommand)
274 {
275 buf_strcpy(cmd, entry->editcommand);
276 mailcap_expand_filename(entry->nametemplate, b->filename, newfile);
277 mutt_debug(LL_DEBUG1, "oldfile: %s newfile: %s\n", b->filename,
278 buf_string(newfile));
279 if (mutt_file_symlink(b->filename, buf_string(newfile)) == -1)
280 {
281 if (query_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
282 goto bailout;
283 buf_strcpy(newfile, b->filename);
284 }
285 else
286 {
287 unlink_newfile = true;
288 }
289
290 if (mailcap_expand_command(b, buf_string(newfile), type, cmd))
291 {
292 /* For now, editing requires a file, no piping */
293 mutt_error(_("Mailcap Edit entry requires %%s"));
294 goto bailout;
295 }
296 else
297 {
298 mutt_endwin();
299 if (mutt_system(buf_string(cmd)) == -1)
300 {
301 mutt_error(_("Error running \"%s\""), buf_string(cmd));
302 goto bailout;
303 }
304 }
305 }
306 }
307 else if (b->type == TYPE_TEXT)
308 {
309 /* On text, default to editor */
310 const char *const c_editor = cs_subset_string(NeoMutt->sub, "editor");
311 mutt_edit_file(NONULL(c_editor), b->filename);
312 }
313 else
314 {
315 mutt_error(_("No mailcap edit entry for %s"), type);
316 goto bailout;
317 }
318
319 rc = true;
320
321bailout:
322
323 if (unlink_newfile)
324 unlink(buf_string(newfile));
325
326 buf_pool_release(&cmd);
327 buf_pool_release(&newfile);
328
329 mailcap_entry_free(&entry);
330 return rc;
331}
332
339void mutt_check_lookup_list(struct Body *b, char *type, size_t len)
340{
342 ASSERT(md);
343
344 struct ListNode *np = NULL;
345 STAILQ_FOREACH(np, &md->mime_lookup, entries)
346 {
347 const int i = (int) 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 *tempfile = 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", BODY_TYPE(b), b->subtype);
447
448 char columns[16] = { 0 };
449 snprintf(columns, sizeof(columns), "%d", win->state.cols);
450 envlist_set(&NeoMutt->env, "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 tempfile. The path will be removed in expand_filename */
485 mutt_file_sanitize_filename(fname, fp ? true : false);
486 mailcap_expand_filename(entry->nametemplate, fname, tempfile);
487 FREE(&fname);
488
489 if (mutt_save_attachment(fp, b, buf_string(tempfile), 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(tempfile, &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(tempfile), 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 &&
546 ((fd_pager = mutt_file_open(buf_string(pagerfile),
547 O_CREAT | O_EXCL | O_WRONLY, 0600)) == -1))
548 {
549 mutt_perror("open");
550 goto return_error;
551 }
552 unlink_pagerfile = true;
553
554 if (use_pipe && ((fd_temp = open(buf_string(tempfile), 0)) == -1))
555 {
556 if (fd_pager != -1)
557 close(fd_pager);
558 mutt_perror("open");
559 goto return_error;
560 }
561 unlink_pagerfile = true;
562
563 pid = filter_create_fd(buf_string(cmd), NULL, NULL, NULL, use_pipe ? fd_temp : -1,
564 use_pager ? fd_pager : -1, -1, NeoMutt->env);
565
566 if (pid == -1)
567 {
568 if (fd_pager != -1)
569 close(fd_pager);
570
571 if (fd_temp != -1)
572 close(fd_temp);
573
574 mutt_error(_("Can't create filter"));
575 goto return_error;
576 }
577
578 if (use_pager)
579 {
580 if (b->description)
581 {
582 snprintf(desc, sizeof(desc), _("---Command: %-20.20s Description: %s"),
583 buf_string(cmd), b->description);
584 }
585 else
586 {
587 snprintf(desc, sizeof(desc), _("---Command: %-30.30s Attachment: %s"),
588 buf_string(cmd), type);
589 }
590 filter_wait(pid);
591 }
592 else
593 {
594 if (wait_interactive_filter(pid) || (entry->needsterminal && c_wait_key))
596 }
597
598 if (fd_temp != -1)
599 close(fd_temp);
600 if (fd_pager != -1)
601 close(fd_pager);
602 }
603 else
604 {
605 /* interactive cmd */
606 int rv = mutt_system(buf_string(cmd));
607 if (rv == -1)
608 mutt_debug(LL_DEBUG1, "Error running \"%s\"\n", cmd->data);
609
610 if ((rv != 0) || (entry->needsterminal && c_wait_key))
612 }
613 }
614 else
615 {
616 /* Don't use mailcap; the attachment is viewed in the pager */
617
618 if (mode == MUTT_VA_AS_TEXT)
619 {
620 /* just let me see the raw data */
621 if (fp)
622 {
623 /* Viewing from a received message.
624 *
625 * Don't use mutt_save_attachment() because we want to perform charset
626 * conversion since this will be displayed by the internal pager. */
627 struct State state = { 0 };
628
629 state.fp_out = mutt_file_fopen(buf_string(pagerfile), "w");
630 if (!state.fp_out)
631 {
632 mutt_debug(LL_DEBUG1, "mutt_file_fopen(%s) errno=%d %s\n",
633 buf_string(pagerfile), errno, strerror(errno));
634 mutt_perror("%s", buf_string(pagerfile));
635 goto return_error;
636 }
637 state.fp_in = fp;
638 state.flags = STATE_CHARCONV;
639 mutt_decode_attachment(b, &state);
640 mutt_file_fclose(&state.fp_out);
641 }
642 else
643 {
644 /* in compose mode, just copy the file. we can't use
645 * mutt_decode_attachment() since it assumes the content-encoding has
646 * already been applied */
647 if (mutt_save_attachment(fp, b, buf_string(pagerfile), MUTT_SAVE_NO_FLAGS, NULL))
648 goto return_error;
649 unlink_pagerfile = true;
650 }
652 }
653 else
654 {
656 const char *const c_pager = pager_get_pager(NeoMutt->sub);
657 if (!c_pager)
659
660 /* Use built-in handler */
662 {
663 goto return_error;
664 }
665 unlink_pagerfile = true;
666 }
667
668 if (b->description)
669 mutt_str_copy(desc, b->description, sizeof(desc));
670 else if (b->filename)
671 snprintf(desc, sizeof(desc), _("---Attachment: %s: %s"), b->filename, type);
672 else
673 snprintf(desc, sizeof(desc), _("---Attachment: %s"), type);
674 }
675
676 /* We only reach this point if there have been no errors */
677
678 if (use_pager)
679 {
680 struct PagerData pdata = { 0 };
681 struct PagerView pview = { &pdata };
682
683 pdata.actx = actx;
684 pdata.body = b;
685 pdata.fname = buf_string(pagerfile);
686 pdata.fp = fp;
687
688 pview.banner = desc;
690 (is_message ? MUTT_PAGER_MESSAGE : MUTT_PAGER_NO_FLAGS) |
691 ((use_mailcap && entry->xneomuttnowrap) ? MUTT_PAGER_NOWRAP :
693 pview.mode = PAGER_MODE_ATTACH;
694
695 rc = mutt_do_pager(&pview, e);
696
697 buf_reset(pagerfile);
698 unlink_pagerfile = false;
699 }
700 else
701 {
702 rc = 0;
703 }
704
705return_error:
706
707 if (!entry || !entry->xneomuttkeep)
708 {
709 if ((fp && !buf_is_empty(tempfile)) || has_tempfile)
710 {
711 /* add temporary file to list of files to be deleted on timeout hook */
713 }
714 }
715
716 mailcap_entry_free(&entry);
717
718 if (unlink_pagerfile)
719 mutt_file_unlink(buf_string(pagerfile));
720
721 buf_pool_release(&tempfile);
722 buf_pool_release(&pagerfile);
723 buf_pool_release(&cmd);
724 envlist_unset(&NeoMutt->env, "COLUMNS");
725
726 return rc;
727}
728
738int mutt_pipe_attachment(FILE *fp, struct Body *b, const char *path, const char *outfile)
739{
740 pid_t pid = 0;
741 int out = -1, rc = 0;
742 bool is_flowed = false;
743 bool unlink_unstuff = false;
744 FILE *fp_filter = NULL, *fp_unstuff = NULL, *fp_in = NULL;
745 struct Buffer *unstuff_tempfile = NULL;
746
747 if (outfile && *outfile)
748 {
749 out = mutt_file_open(outfile, O_CREAT | O_EXCL | O_WRONLY, 0600);
750 if (out < 0)
751 {
752 mutt_perror("open");
753 return 0;
754 }
755 }
756
758 {
759 is_flowed = true;
760 unstuff_tempfile = buf_pool_get();
761 buf_mktemp(unstuff_tempfile);
762 }
763
764 mutt_endwin();
765
766 if (outfile && *outfile)
767 pid = filter_create_fd(path, &fp_filter, NULL, NULL, -1, out, -1, NeoMutt->env);
768 else
769 pid = filter_create(path, &fp_filter, NULL, NULL, NeoMutt->env);
770 if (pid < 0)
771 {
772 mutt_perror(_("Can't create filter"));
773 goto bail;
774 }
775
776 /* recv case */
777 if (fp)
778 {
779 struct State state = { 0 };
780
781 /* perform charset conversion on text attachments when piping */
782 state.flags = STATE_CHARCONV;
783
784 if (is_flowed)
785 {
786 fp_unstuff = mutt_file_fopen(buf_string(unstuff_tempfile), "w");
787 if (!fp_unstuff)
788 {
789 mutt_perror("mutt_file_fopen");
790 goto bail;
791 }
792 unlink_unstuff = true;
793
794 state.fp_in = fp;
795 state.fp_out = fp_unstuff;
796 mutt_decode_attachment(b, &state);
797 mutt_file_fclose(&fp_unstuff);
798
800
801 fp_unstuff = mutt_file_fopen(buf_string(unstuff_tempfile), "r");
802 if (!fp_unstuff)
803 {
804 mutt_perror("mutt_file_fopen");
805 goto bail;
806 }
807 mutt_file_copy_stream(fp_unstuff, fp_filter);
808 mutt_file_fclose(&fp_unstuff);
809 }
810 else
811 {
812 state.fp_in = fp;
813 state.fp_out = fp_filter;
814 mutt_decode_attachment(b, &state);
815 }
816 }
817 else
818 {
819 /* send case */
820 const char *infile = NULL;
821
822 if (is_flowed)
823 {
824 if (mutt_save_attachment(fp, b, buf_string(unstuff_tempfile),
825 MUTT_SAVE_NO_FLAGS, NULL) == -1)
826 {
827 goto bail;
828 }
829 unlink_unstuff = true;
831 infile = buf_string(unstuff_tempfile);
832 }
833 else
834 {
835 infile = b->filename;
836 }
837
838 fp_in = mutt_file_fopen(infile, "r");
839 if (!fp_in)
840 {
841 mutt_perror("fopen");
842 goto bail;
843 }
844
845 mutt_file_copy_stream(fp_in, fp_filter);
847 }
848
849 mutt_file_fclose(&fp_filter);
850 rc = 1;
851
852bail:
853 if (outfile && *outfile)
854 {
855 close(out);
856 if (rc == 0)
857 unlink(outfile);
858 else if (is_flowed)
860 }
861
862 mutt_file_fclose(&fp_unstuff);
863 mutt_file_fclose(&fp_filter);
865
866 if (unlink_unstuff)
867 mutt_file_unlink(buf_string(unstuff_tempfile));
868 buf_pool_release(&unstuff_tempfile);
869
870 /* check for error exit from child process */
871 if ((pid > 0) && (filter_wait(pid) != 0))
872 rc = 0;
873
874 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
875 if ((rc == 0) || c_wait_key)
877 return rc;
878}
879
886static FILE *save_attachment_open(const char *path, enum SaveAttach opt)
887{
888 if (opt == MUTT_SAVE_APPEND)
889 return mutt_file_fopen_masked(path, "a");
890 else
891 return mutt_file_fopen_masked(path, "w");
892}
893
904int mutt_save_attachment(FILE *fp, struct Body *b, const char *path,
905 enum SaveAttach opt, struct Email *e)
906{
907 if (!b)
908 return -1;
909
910 if (fp)
911 {
912 /* recv mode */
913
914 if (e && b->email && (b->encoding != ENC_BASE64) &&
916 {
917 /* message type attachments are written to mail folders. */
918
919 char buf[8192] = { 0 };
920 struct Message *msg = NULL;
922 int rc = -1;
923
924 struct Email *e_new = b->email;
925 e_new->msgno = e->msgno; /* required for MH/maildir */
926 e_new->read = true;
927
928 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
929 return -1;
930 if (!fgets(buf, sizeof(buf), fp))
931 return -1;
932 struct Mailbox *m_att = mx_path_resolve(path);
933 if (!mx_mbox_open(m_att, MUTT_APPEND | MUTT_QUIET))
934 {
935 mailbox_free(&m_att);
936 return -1;
937 }
938 msg = mx_msg_open_new(m_att, e_new,
939 is_from(buf, NULL, 0, NULL) ? MUTT_MSG_NO_FLAGS : MUTT_ADD_FROM);
940 if (!msg)
941 {
942 mx_mbox_close(m_att);
943 return -1;
944 }
945 if ((m_att->type == MUTT_MBOX) || (m_att->type == MUTT_MMDF))
946 chflags = CH_FROM | CH_UPDATE_LEN;
947 chflags |= ((m_att->type == MUTT_MAILDIR) ? CH_NOSTATUS : CH_UPDATE);
948 if ((mutt_copy_message_fp(msg->fp, fp, e_new, MUTT_CM_NO_FLAGS, chflags, 0) == 0) &&
949 (mx_msg_commit(m_att, msg) == 0))
950 {
951 rc = 0;
952 }
953 else
954 {
955 rc = -1;
956 }
957
958 mx_msg_close(m_att, &msg);
959 mx_mbox_close(m_att);
960 return rc;
961 }
962 else
963 {
964 /* In recv mode, extract from folder and decode */
965
966 struct State state = { 0 };
967
968 state.fp_out = save_attachment_open(path, opt);
969 if (!state.fp_out)
970 {
971 mutt_perror("fopen");
972 return -1;
973 }
974 if (!mutt_file_seek((state.fp_in = fp), b->offset, SEEK_SET))
975 {
976 mutt_file_fclose(&state.fp_out);
977 return -1;
978 }
979 mutt_decode_attachment(b, &state);
980
981 if (mutt_file_fsync_close(&state.fp_out) != 0)
982 {
983 mutt_perror("fclose");
984 return -1;
985 }
986 }
987 }
988 else
989 {
990 if (!b->filename)
991 return -1;
992
993 /* In send mode, just copy file */
994
995 FILE *fp_old = mutt_file_fopen(b->filename, "r");
996 if (!fp_old)
997 {
998 mutt_perror("fopen");
999 return -1;
1000 }
1001
1002 FILE *fp_new = save_attachment_open(path, opt);
1003 if (!fp_new)
1004 {
1005 mutt_perror("fopen");
1006 mutt_file_fclose(&fp_old);
1007 return -1;
1008 }
1009
1010 if (mutt_file_copy_stream(fp_old, fp_new) == -1)
1011 {
1012 mutt_error(_("Write fault"));
1013 mutt_file_fclose(&fp_old);
1014 mutt_file_fclose(&fp_new);
1015 return -1;
1016 }
1017 mutt_file_fclose(&fp_old);
1018 if (mutt_file_fsync_close(&fp_new) != 0)
1019 {
1020 mutt_error(_("Write fault"));
1021 return -1;
1022 }
1023 }
1024
1025 return 0;
1026}
1027
1038int mutt_decode_save_attachment(FILE *fp, struct Body *b, const char *path,
1039 StateFlags flags, enum SaveAttach opt)
1040{
1041 struct State state = { 0 };
1042 unsigned int saved_encoding = 0;
1043 struct Body *saved_parts = NULL;
1044 struct Email *e_saved = NULL;
1045 int rc = 0;
1046
1047 state.flags = flags;
1048
1049 if (opt == MUTT_SAVE_APPEND)
1050 state.fp_out = mutt_file_fopen_masked(path, "a");
1051 else
1052 state.fp_out = mutt_file_fopen_masked(path, "w");
1053
1054 if (!state.fp_out)
1055 {
1056 mutt_perror("fopen");
1057 return -1;
1058 }
1059
1060 if (fp)
1061 {
1062 state.fp_in = fp;
1063 state.flags |= STATE_CHARCONV;
1064 }
1065 else
1066 {
1067 /* When called from the compose menu, the attachment isn't parsed,
1068 * so we need to do it here. */
1069 state.fp_in = mutt_file_fopen(b->filename, "r");
1070 if (!state.fp_in)
1071 {
1072 mutt_perror("fopen");
1073 mutt_file_fclose(&state.fp_out);
1074 return -1;
1075 }
1076
1077 struct stat st = { 0 };
1078 if (fstat(fileno(state.fp_in), &st) == -1)
1079 {
1080 mutt_perror("stat");
1081 mutt_file_fclose(&state.fp_in);
1082 mutt_file_fclose(&state.fp_out);
1083 return -1;
1084 }
1085
1086 saved_encoding = b->encoding;
1087 if (!is_multipart(b))
1088 b->encoding = ENC_8BIT;
1089
1090 b->length = st.st_size;
1091 b->offset = 0;
1092 saved_parts = b->parts;
1093 e_saved = b->email;
1094 mutt_parse_part(state.fp_in, b);
1095
1096 if (b->noconv || is_multipart(b))
1097 state.flags |= STATE_CHARCONV;
1098 }
1099
1100 mutt_body_handler(b, &state);
1101
1102 if (mutt_file_fsync_close(&state.fp_out) != 0)
1103 {
1104 mutt_perror("fclose");
1105 rc = -1;
1106 }
1107 if (!fp)
1108 {
1109 b->length = 0;
1110 b->encoding = saved_encoding;
1111 if (saved_parts)
1112 {
1113 email_free(&b->email);
1114 b->parts = saved_parts;
1115 b->email = e_saved;
1116 }
1117 mutt_file_fclose(&state.fp_in);
1118 }
1119
1120 return rc;
1121}
1122
1136int mutt_print_attachment(FILE *fp, struct Body *b)
1137{
1138 char type[256] = { 0 };
1139 pid_t pid;
1140 FILE *fp_in = NULL, *fp_out = NULL;
1141 bool unlink_newfile = false;
1142 struct Buffer *newfile = buf_pool_get();
1143 struct Buffer *cmd = buf_pool_get();
1144
1145 int rc = 0;
1146
1147 snprintf(type, sizeof(type), "%s/%s", BODY_TYPE(b), b->subtype);
1148
1149 if (mailcap_lookup(b, type, sizeof(type), NULL, MUTT_MC_PRINT))
1150 {
1151 mutt_debug(LL_DEBUG2, "Using mailcap\n");
1152
1153 struct MailcapEntry *entry = mailcap_entry_new();
1154 mailcap_lookup(b, type, sizeof(type), entry, MUTT_MC_PRINT);
1155
1156 char *sanitized_fname = mutt_str_dup(b->filename);
1157 /* In send mode (!fp), we allow slashes because those are part of
1158 * the tempfile. The path will be removed in expand_filename */
1159 mutt_file_sanitize_filename(sanitized_fname, fp ? true : false);
1160 mailcap_expand_filename(entry->nametemplate, sanitized_fname, newfile);
1161 FREE(&sanitized_fname);
1162
1163 if (mutt_save_attachment(fp, b, buf_string(newfile), MUTT_SAVE_NO_FLAGS, NULL) == -1)
1164 {
1165 goto mailcap_cleanup;
1166 }
1167 unlink_newfile = 1;
1168
1170
1171 buf_strcpy(cmd, entry->printcommand);
1172
1173 bool piped = mailcap_expand_command(b, buf_string(newfile), type, cmd);
1174
1175 mutt_endwin();
1176
1177 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
1178 /* interactive program */
1179 if (piped)
1180 {
1181 fp_in = mutt_file_fopen(buf_string(newfile), "r");
1182 if (!fp_in)
1183 {
1184 mutt_perror("fopen");
1185 mailcap_entry_free(&entry);
1186 goto mailcap_cleanup;
1187 }
1188
1189 pid = filter_create(buf_string(cmd), &fp_out, NULL, NULL, NeoMutt->env);
1190 if (pid < 0)
1191 {
1192 mutt_perror(_("Can't create filter"));
1193 mailcap_entry_free(&entry);
1194 mutt_file_fclose(&fp_in);
1195 goto mailcap_cleanup;
1196 }
1197 mutt_file_copy_stream(fp_in, fp_out);
1198 mutt_file_fclose(&fp_out);
1199 mutt_file_fclose(&fp_in);
1200 if (filter_wait(pid) || c_wait_key)
1202 }
1203 else
1204 {
1205 int rc2 = mutt_system(buf_string(cmd));
1206 if (rc2 == -1)
1207 mutt_debug(LL_DEBUG1, "Error running \"%s\"\n", cmd->data);
1208
1209 if ((rc2 != 0) || c_wait_key)
1211 }
1212
1213 rc = 1;
1214
1215 mailcap_cleanup:
1216 if (unlink_newfile)
1217 mutt_file_unlink(buf_string(newfile));
1218
1219 mailcap_entry_free(&entry);
1220 goto out;
1221 }
1222
1223 const char *const c_print_command = cs_subset_string(NeoMutt->sub, "print_command");
1224 if (mutt_istr_equal("text/plain", type) || mutt_istr_equal("application/postscript", type))
1225 {
1226 rc = (mutt_pipe_attachment(fp, b, NONULL(c_print_command), NULL));
1227 goto out;
1228 }
1229 else if (mutt_can_decode(b))
1230 {
1231 /* decode and print */
1232
1233 fp_in = NULL;
1234 fp_out = NULL;
1235
1236 buf_mktemp(newfile);
1238 MUTT_SAVE_NO_FLAGS) == 0)
1239 {
1240 unlink_newfile = true;
1241 mutt_debug(LL_DEBUG2, "successfully decoded %s type attachment to %s\n",
1242 type, buf_string(newfile));
1243
1244 fp_in = mutt_file_fopen(buf_string(newfile), "r");
1245 if (!fp_in)
1246 {
1247 mutt_perror("fopen");
1248 goto decode_cleanup;
1249 }
1250
1251 mutt_debug(LL_DEBUG2, "successfully opened %s read-only\n", buf_string(newfile));
1252
1253 mutt_endwin();
1254 pid = filter_create(NONULL(c_print_command), &fp_out, NULL, NULL, NeoMutt->env);
1255 if (pid < 0)
1256 {
1257 mutt_perror(_("Can't create filter"));
1258 goto decode_cleanup;
1259 }
1260
1261 mutt_debug(LL_DEBUG2, "Filter created\n");
1262
1263 mutt_file_copy_stream(fp_in, fp_out);
1264
1265 mutt_file_fclose(&fp_out);
1266 mutt_file_fclose(&fp_in);
1267
1268 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
1269 if ((filter_wait(pid) != 0) || c_wait_key)
1271 rc = 1;
1272 }
1273 decode_cleanup:
1274 mutt_file_fclose(&fp_in);
1275 mutt_file_fclose(&fp_out);
1276 if (unlink_newfile)
1277 mutt_file_unlink(buf_string(newfile));
1278 }
1279 else
1280 {
1281 mutt_error(_("I don't know how to print that"));
1282 rc = 0;
1283 }
1284
1285out:
1286 buf_pool_release(&newfile);
1287 buf_pool_release(&cmd);
1288
1289 return rc;
1290}
1291
1296void mutt_add_temp_attachment(const char *filename)
1297{
1299 ASSERT(md);
1300
1302}
1303
1308{
1310 ASSERT(md);
1311
1312 struct ListNode *np = NULL;
1313
1314 STAILQ_FOREACH(np, &md->temp_attachments, entries)
1315 {
1316 (void) mutt_file_chmod_add(np->data, S_IWUSR);
1318 }
1319
1321}
struct Body * attach_body_ancestor(struct Body *start, struct Body *body, const char *subtype)
Find the ancestor of a body with specified subtype.
Definition lib.c:116
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:76
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
void cid_save_attachments(struct Body *body, struct CidMapList *cid_map_list)
Save all attachments in a "multipart/related" group with a Content-ID.
Definition cid.c: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
#define CH_UPDATE
Update the status and x-status fields?
Definition copy_email.h:56
#define CH_NOSTATUS
Suppress the status and x-status fields.
Definition copy_email.h:62
#define CH_FROM
Retain the "From " message separator?
Definition copy_email.h:60
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition copy_email.h:54
#define CH_UPDATE_LEN
Update Lines: and Content-Length:
Definition copy_email.h:66
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition copy_email.h:37
#define CH_NO_FLAGS
No flags are set.
Definition copy_email.h:55
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:116
int mutt_any_key_to_continue(const char *s)
Prompt the user to 'press any key' and wait.
Definition curs_lib.c:173
void mutt_endwin(void)
Shutdown curses.
Definition curs_lib.c:151
int mutt_do_pager(struct PagerView *pview, struct Email *e)
Display some page-able text to the user (help or attachment)
Definition do_pager.c:122
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition body.c:58
void email_free(struct Email **ptr)
Free an Email.
Definition email.c:46
Structs that make up an email.
void mutt_parse_part(FILE *fp, struct Body *b)
Parse a MIME part.
Definition parse.c:1833
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition parse.c:1370
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition parse.c:1506
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:222
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition file.c:586
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition file.c:652
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition file.c:1261
int mutt_file_symlink(const char *oldpath, const char *newpath)
Create a symlink.
Definition file.c:250
int mutt_file_chmod_add(const char *path, mode_t mode)
Add permissions to a file.
Definition file.c:1006
int mutt_file_open(const char *path, uint32_t flags, mode_t mode)
Open a file.
Definition file.c:512
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:139
#define mutt_file_fopen(PATH, MODE)
Definition file.h:138
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:1893
int mutt_body_handler(struct Body *b, struct State *state)
Handler for the Body of an email.
Definition handler.c:1659
void mutt_decode_attachment(const struct Body *b, struct State *state)
Decode an email's attachment.
Definition handler.c:1933
IMAP network mailbox.
int imap_wait_keep_alive(pid_t pid)
Wait for a process to change state.
Definition util.c:1021
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:553
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_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)
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:220
pid_t filter_create_fd(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err, int fdin, int fdout, int fderr, char **envlist)
Run a command on a pipe (optionally connect stdin/stdout)
Definition filter.c:62
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err, char **envlist)
Set up filter program.
Definition filter.c:209
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
#define STATE_PAGER
Output will be displayed in the Pager.
Definition state.h:42
#define STATE_DISPLAY
Output is displayed to the user.
Definition state.h:33
#define STATE_DISPLAY_ATTACH
We are displaying an attachment.
Definition state.h:41
#define STATE_CHARCONV
Do character set conversions.
Definition state.h:37
uint16_t StateFlags
Flags for State->flags, e.g. STATE_DISPLAY.
Definition state.h:31
#define STATE_PRINTING
Are we printing? - STATE_DISPLAY "light".
Definition state.h:38
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:674
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:662
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:500
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:583
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
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.
void mutt_temp_attachments_cleanup(void)
Delete all temporary attachments.
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:57
@ MUTT_SAVE_APPEND
Append to existing file.
Definition mutt_attach.h:59
@ MUTT_SAVE_NO_FLAGS
Overwrite existing file (the default)
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:367
void mutt_adv_mktemp(struct Buffer *buf)
Create a temporary file.
Definition muttlib.c:83
Some miscellaneous functions.
int mx_msg_close(struct Mailbox *m, struct Message **ptr)
Close a message.
Definition mx.c:1182
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:1041
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition mx.c:1161
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition mx.c:1647
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition mx.c:595
API for mailboxes.
#define MUTT_ADD_FROM
add a From_ line
Definition mx.h:39
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition mx.h:38
#define MUTT_APPEND
Open mailbox for appending messages.
Definition mxapi.h:41
#define MUTT_QUIET
Do not print any messages.
Definition mxapi.h:43
API for encryption/signing of emails.
#define SEC_ENCRYPT
Email is encrypted.
Definition lib.h:86
#define WithCrypto
Definition lib.h:124
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:585
#define mutt_file_fopen_masked(PATH, MODE)
Open a file with proper permissions, tracked for debugging.
Definition neomutt.h:90
const char * pager_get_pager(struct ConfigSubset *sub)
Get the value of $pager.
Definition config.c:111
GUI display a file/email/help in a viewport with paging.
#define MUTT_PAGER_NO_FLAGS
No flags are set.
Definition lib.h:62
#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: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:326
#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:543
void mutt_rfc3676_space_unstuff_attachment(struct Body *b, const char *filename)
Unstuff attachments.
Definition rfc3676.c:522
bool mutt_rfc3676_is_format_flowed(struct Body *b)
Is the Email "format-flowed"?
Definition rfc3676.c:398
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
#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:63
struct Body ** body_idx
Extra struct Body* used for decryption.
Definition attach.h:78
Attach private Module data.
Definition module_data.h:32
struct ListHead temp_attachments
List of temporary files for displaying attachments.
Definition module_data.h:40
struct ListHead mime_lookup
List of mime types that that shouldn't use the mailcap entry.
Definition module_data.h:39
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:78
enum MailboxType type
Mailbox type.
Definition mailbox.h:101
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:58
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
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