NeoMutt  2025-12-11-911-gd8d604
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, *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(mod_data);
343
344 struct ListNode *np = NULL;
345 STAILQ_FOREACH(np, &mod_data->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_NONE;
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_NONE, 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_NONE) |
691 ((use_mailcap && entry->xneomuttnowrap) ? MUTT_PAGER_NOWRAP : MUTT_PAGER_NONE);
692 pview.mode = PAGER_MODE_ATTACH;
693
694 rc = mutt_do_pager(&pview, e);
695
696 buf_reset(pagerfile);
697 unlink_pagerfile = false;
698 }
699 else
700 {
701 rc = 0;
702 }
703
704return_error:
705
706 if (!entry || !entry->xneomuttkeep)
707 {
708 if ((fp && !buf_is_empty(tempfile)) || has_tempfile)
709 {
710 /* add temporary file to list of files to be deleted on timeout hook */
712 }
713 }
714
715 mailcap_entry_free(&entry);
716
717 if (unlink_pagerfile)
718 mutt_file_unlink(buf_string(pagerfile));
719
720 buf_pool_release(&tempfile);
721 buf_pool_release(&pagerfile);
722 buf_pool_release(&cmd);
723 envlist_unset(&NeoMutt->env, "COLUMNS");
724
725 return rc;
726}
727
737int mutt_pipe_attachment(FILE *fp, struct Body *b, const char *path, const char *outfile)
738{
739 pid_t pid = 0;
740 int out = -1, rc = 0;
741 bool is_flowed = false;
742 bool unlink_unstuff = false;
743 FILE *fp_filter = NULL, *fp_unstuff = NULL, *fp_in = NULL;
744 struct Buffer *unstuff_tempfile = NULL;
745
746 if (outfile && *outfile)
747 {
748 out = mutt_file_open(outfile, O_CREAT | O_EXCL | O_WRONLY, 0600);
749 if (out < 0)
750 {
751 mutt_perror("open");
752 return 0;
753 }
754 }
755
757 {
758 is_flowed = true;
759 unstuff_tempfile = buf_pool_get();
760 buf_mktemp(unstuff_tempfile);
761 }
762
763 mutt_endwin();
764
765 if (outfile && *outfile)
766 pid = filter_create_fd(path, &fp_filter, NULL, NULL, -1, out, -1, NeoMutt->env);
767 else
768 pid = filter_create(path, &fp_filter, NULL, NULL, NeoMutt->env);
769 if (pid < 0)
770 {
771 mutt_perror(_("Can't create filter"));
772 goto bail;
773 }
774
775 /* recv case */
776 if (fp)
777 {
778 struct State state = { 0 };
779
780 /* perform charset conversion on text attachments when piping */
781 state.flags = STATE_CHARCONV;
782
783 if (is_flowed)
784 {
785 fp_unstuff = mutt_file_fopen(buf_string(unstuff_tempfile), "w");
786 if (!fp_unstuff)
787 {
788 mutt_perror("mutt_file_fopen");
789 goto bail;
790 }
791 unlink_unstuff = true;
792
793 state.fp_in = fp;
794 state.fp_out = fp_unstuff;
795 mutt_decode_attachment(b, &state);
796 mutt_file_fclose(&fp_unstuff);
797
799
800 fp_unstuff = mutt_file_fopen(buf_string(unstuff_tempfile), "r");
801 if (!fp_unstuff)
802 {
803 mutt_perror("mutt_file_fopen");
804 goto bail;
805 }
806 mutt_file_copy_stream(fp_unstuff, fp_filter);
807 mutt_file_fclose(&fp_unstuff);
808 }
809 else
810 {
811 state.fp_in = fp;
812 state.fp_out = fp_filter;
813 mutt_decode_attachment(b, &state);
814 }
815 }
816 else
817 {
818 /* send case */
819 const char *infile = NULL;
820
821 if (is_flowed)
822 {
823 if (mutt_save_attachment(fp, b, buf_string(unstuff_tempfile),
824 MUTT_SAVE_NONE, NULL) == -1)
825 {
826 goto bail;
827 }
828 unlink_unstuff = true;
830 infile = buf_string(unstuff_tempfile);
831 }
832 else
833 {
834 infile = b->filename;
835 }
836
837 fp_in = mutt_file_fopen(infile, "r");
838 if (!fp_in)
839 {
840 mutt_perror("fopen");
841 goto bail;
842 }
843
844 mutt_file_copy_stream(fp_in, fp_filter);
846 }
847
848 mutt_file_fclose(&fp_filter);
849 rc = 1;
850
851bail:
852 if (outfile && *outfile)
853 {
854 close(out);
855 if (rc == 0)
856 unlink(outfile);
857 else if (is_flowed)
859 }
860
861 mutt_file_fclose(&fp_unstuff);
862 mutt_file_fclose(&fp_filter);
864
865 if (unlink_unstuff)
866 mutt_file_unlink(buf_string(unstuff_tempfile));
867 buf_pool_release(&unstuff_tempfile);
868
869 /* check for error exit from child process */
870 if ((pid > 0) && (filter_wait(pid) != 0))
871 rc = 0;
872
873 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
874 if ((rc == 0) || c_wait_key)
876 return rc;
877}
878
885static FILE *save_attachment_open(const char *path, enum SaveAttach opt)
886{
887 if (opt == MUTT_SAVE_APPEND)
888 return mutt_file_fopen_masked(path, "a");
889 else
890 return mutt_file_fopen_masked(path, "w");
891}
892
903int mutt_save_attachment(FILE *fp, struct Body *b, const char *path,
904 enum SaveAttach opt, struct Email *e)
905{
906 if (!b)
907 return -1;
908
909 if (fp)
910 {
911 /* recv mode */
912
913 if (e && b->email && (b->encoding != ENC_BASE64) &&
915 {
916 /* message type attachments are written to mail folders. */
917
918 char buf[8192] = { 0 };
919 struct Message *msg = NULL;
920 CopyHeaderFlags chflags = CH_NONE;
921 int rc = -1;
922
923 struct Email *e_new = b->email;
924 e_new->msgno = e->msgno; /* required for MH/maildir */
925 e_new->read = true;
926
927 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
928 return -1;
929 if (!fgets(buf, sizeof(buf), fp))
930 return -1;
931 struct Mailbox *m_att = mx_path_resolve(path);
932 if (!mx_mbox_open(m_att, MUTT_APPEND | MUTT_QUIET))
933 {
934 mailbox_free(&m_att);
935 return -1;
936 }
937 msg = mx_msg_open_new(m_att, e_new, is_from(buf, NULL, 0, NULL) ? MUTT_MSG_NONE : MUTT_ADD_FROM);
938 if (!msg)
939 {
940 mx_mbox_close(m_att);
941 return -1;
942 }
943 if ((m_att->type == MUTT_MBOX) || (m_att->type == MUTT_MMDF))
944 chflags = CH_FROM | CH_UPDATE_LEN;
945 chflags |= ((m_att->type == MUTT_MAILDIR) ? CH_NOSTATUS : CH_UPDATE);
946 if ((mutt_copy_message_fp(msg->fp, fp, e_new, MUTT_CM_NONE, chflags, 0) == 0) &&
947 (mx_msg_commit(m_att, msg) == 0))
948 {
949 rc = 0;
950 }
951 else
952 {
953 rc = -1;
954 }
955
956 mx_msg_close(m_att, &msg);
957 mx_mbox_close(m_att);
958 return rc;
959 }
960 else
961 {
962 /* In recv mode, extract from folder and decode */
963
964 struct State state = { 0 };
965
966 state.fp_out = save_attachment_open(path, opt);
967 if (!state.fp_out)
968 {
969 mutt_perror("fopen");
970 return -1;
971 }
972 if (!mutt_file_seek((state.fp_in = fp), b->offset, SEEK_SET))
973 {
974 mutt_file_fclose(&state.fp_out);
975 return -1;
976 }
977 mutt_decode_attachment(b, &state);
978
979 if (mutt_file_fsync_close(&state.fp_out) != 0)
980 {
981 mutt_perror("fclose");
982 return -1;
983 }
984 }
985 }
986 else
987 {
988 if (!b->filename)
989 return -1;
990
991 /* In send mode, just copy file */
992
993 FILE *fp_old = mutt_file_fopen(b->filename, "r");
994 if (!fp_old)
995 {
996 mutt_perror("fopen");
997 return -1;
998 }
999
1000 FILE *fp_new = save_attachment_open(path, opt);
1001 if (!fp_new)
1002 {
1003 mutt_perror("fopen");
1004 mutt_file_fclose(&fp_old);
1005 return -1;
1006 }
1007
1008 if (mutt_file_copy_stream(fp_old, fp_new) == -1)
1009 {
1010 mutt_error(_("Write fault"));
1011 mutt_file_fclose(&fp_old);
1012 mutt_file_fclose(&fp_new);
1013 return -1;
1014 }
1015 mutt_file_fclose(&fp_old);
1016 if (mutt_file_fsync_close(&fp_new) != 0)
1017 {
1018 mutt_error(_("Write fault"));
1019 return -1;
1020 }
1021 }
1022
1023 return 0;
1024}
1025
1036int mutt_decode_save_attachment(FILE *fp, struct Body *b, const char *path,
1037 StateFlags flags, enum SaveAttach opt)
1038{
1039 struct State state = { 0 };
1040 unsigned int saved_encoding = 0;
1041 struct Body *saved_parts = NULL;
1042 struct Email *e_saved = NULL;
1043 int rc = 0;
1044
1045 state.flags = flags;
1046
1047 if (opt == MUTT_SAVE_APPEND)
1048 state.fp_out = mutt_file_fopen_masked(path, "a");
1049 else
1050 state.fp_out = mutt_file_fopen_masked(path, "w");
1051
1052 if (!state.fp_out)
1053 {
1054 mutt_perror("fopen");
1055 return -1;
1056 }
1057
1058 if (fp)
1059 {
1060 state.fp_in = fp;
1061 state.flags |= STATE_CHARCONV;
1062 }
1063 else
1064 {
1065 /* When called from the compose menu, the attachment isn't parsed,
1066 * so we need to do it here. */
1067 state.fp_in = mutt_file_fopen(b->filename, "r");
1068 if (!state.fp_in)
1069 {
1070 mutt_perror("fopen");
1071 mutt_file_fclose(&state.fp_out);
1072 return -1;
1073 }
1074
1075 struct stat st = { 0 };
1076 if (fstat(fileno(state.fp_in), &st) == -1)
1077 {
1078 mutt_perror("stat");
1079 mutt_file_fclose(&state.fp_in);
1080 mutt_file_fclose(&state.fp_out);
1081 return -1;
1082 }
1083
1084 saved_encoding = b->encoding;
1085 if (!is_multipart(b))
1086 b->encoding = ENC_8BIT;
1087
1088 b->length = st.st_size;
1089 b->offset = 0;
1090 saved_parts = b->parts;
1091 e_saved = b->email;
1092 mutt_parse_part(state.fp_in, b);
1093
1094 if (b->noconv || is_multipart(b))
1095 state.flags |= STATE_CHARCONV;
1096 }
1097
1098 mutt_body_handler(b, &state);
1099
1100 if (mutt_file_fsync_close(&state.fp_out) != 0)
1101 {
1102 mutt_perror("fclose");
1103 rc = -1;
1104 }
1105 if (!fp)
1106 {
1107 b->length = 0;
1108 b->encoding = saved_encoding;
1109 if (saved_parts)
1110 {
1111 email_free(&b->email);
1112 b->parts = saved_parts;
1113 b->email = e_saved;
1114 }
1115 mutt_file_fclose(&state.fp_in);
1116 }
1117
1118 return rc;
1119}
1120
1134int mutt_print_attachment(FILE *fp, struct Body *b)
1135{
1136 char type[256] = { 0 };
1137 pid_t pid;
1138 FILE *fp_in = NULL, *fp_out = NULL;
1139 bool unlink_newfile = false;
1140 struct Buffer *newfile = buf_pool_get();
1141 struct Buffer *cmd = buf_pool_get();
1142
1143 int rc = 0;
1144
1145 snprintf(type, sizeof(type), "%s/%s", BODY_TYPE(b), b->subtype);
1146
1147 if (mailcap_lookup(b, type, sizeof(type), NULL, MUTT_MC_PRINT))
1148 {
1149 mutt_debug(LL_DEBUG2, "Using mailcap\n");
1150
1151 struct MailcapEntry *entry = mailcap_entry_new();
1152 mailcap_lookup(b, type, sizeof(type), entry, MUTT_MC_PRINT);
1153
1154 char *sanitized_fname = mutt_str_dup(b->filename);
1155 /* In send mode (!fp), we allow slashes because those are part of
1156 * the tempfile. The path will be removed in expand_filename */
1157 mutt_file_sanitize_filename(sanitized_fname, fp ? true : false);
1158 mailcap_expand_filename(entry->nametemplate, sanitized_fname, newfile);
1159 FREE(&sanitized_fname);
1160
1161 if (mutt_save_attachment(fp, b, buf_string(newfile), MUTT_SAVE_NONE, NULL) == -1)
1162 {
1163 goto mailcap_cleanup;
1164 }
1165 unlink_newfile = 1;
1166
1168
1169 buf_strcpy(cmd, entry->printcommand);
1170
1171 bool piped = mailcap_expand_command(b, buf_string(newfile), type, cmd);
1172
1173 mutt_endwin();
1174
1175 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
1176 /* interactive program */
1177 if (piped)
1178 {
1179 fp_in = mutt_file_fopen(buf_string(newfile), "r");
1180 if (!fp_in)
1181 {
1182 mutt_perror("fopen");
1183 mailcap_entry_free(&entry);
1184 goto mailcap_cleanup;
1185 }
1186
1187 pid = filter_create(buf_string(cmd), &fp_out, NULL, NULL, NeoMutt->env);
1188 if (pid < 0)
1189 {
1190 mutt_perror(_("Can't create filter"));
1191 mailcap_entry_free(&entry);
1192 mutt_file_fclose(&fp_in);
1193 goto mailcap_cleanup;
1194 }
1195 mutt_file_copy_stream(fp_in, fp_out);
1196 mutt_file_fclose(&fp_out);
1197 mutt_file_fclose(&fp_in);
1198 if (filter_wait(pid) || c_wait_key)
1200 }
1201 else
1202 {
1203 int rc2 = mutt_system(buf_string(cmd));
1204 if (rc2 == -1)
1205 mutt_debug(LL_DEBUG1, "Error running \"%s\"\n", cmd->data);
1206
1207 if ((rc2 != 0) || c_wait_key)
1209 }
1210
1211 rc = 1;
1212
1213 mailcap_cleanup:
1214 if (unlink_newfile)
1215 mutt_file_unlink(buf_string(newfile));
1216
1217 mailcap_entry_free(&entry);
1218 goto out;
1219 }
1220
1221 const char *const c_print_command = cs_subset_string(NeoMutt->sub, "print_command");
1222 if (mutt_istr_equal("text/plain", type) || mutt_istr_equal("application/postscript", type))
1223 {
1224 rc = (mutt_pipe_attachment(fp, b, NONULL(c_print_command), NULL));
1225 goto out;
1226 }
1227 else if (mutt_can_decode(b))
1228 {
1229 /* decode and print */
1230
1231 fp_in = NULL;
1232 fp_out = NULL;
1233
1234 buf_mktemp(newfile);
1236 MUTT_SAVE_NONE) == 0)
1237 {
1238 unlink_newfile = true;
1239 mutt_debug(LL_DEBUG2, "successfully decoded %s type attachment to %s\n",
1240 type, buf_string(newfile));
1241
1242 fp_in = mutt_file_fopen(buf_string(newfile), "r");
1243 if (!fp_in)
1244 {
1245 mutt_perror("fopen");
1246 goto decode_cleanup;
1247 }
1248
1249 mutt_debug(LL_DEBUG2, "successfully opened %s read-only\n", buf_string(newfile));
1250
1251 mutt_endwin();
1252 pid = filter_create(NONULL(c_print_command), &fp_out, NULL, NULL, NeoMutt->env);
1253 if (pid < 0)
1254 {
1255 mutt_perror(_("Can't create filter"));
1256 goto decode_cleanup;
1257 }
1258
1259 mutt_debug(LL_DEBUG2, "Filter created\n");
1260
1261 mutt_file_copy_stream(fp_in, fp_out);
1262
1263 mutt_file_fclose(&fp_out);
1264 mutt_file_fclose(&fp_in);
1265
1266 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
1267 if ((filter_wait(pid) != 0) || c_wait_key)
1269 rc = 1;
1270 }
1271 decode_cleanup:
1272 mutt_file_fclose(&fp_in);
1273 mutt_file_fclose(&fp_out);
1274 if (unlink_newfile)
1275 mutt_file_unlink(buf_string(newfile));
1276 }
1277 else
1278 {
1279 mutt_error(_("I don't know how to print that"));
1280 rc = 0;
1281 }
1282
1283out:
1284 buf_pool_release(&newfile);
1285 buf_pool_release(&cmd);
1286
1287 return rc;
1288}
1289
1294void mutt_add_temp_attachment(const char *filename)
1295{
1297 ASSERT(mod_data);
1298
1300}
1301
1306void mutt_temp_attachments_cleanup(struct ListHead *list)
1307{
1308 if (!list)
1309 return;
1310
1311 struct ListNode *np = NULL;
1312
1313 STAILQ_FOREACH(np, list, entries)
1314 {
1315 (void) mutt_file_chmod_add(np->data, S_IWUSR);
1317 }
1318
1319 mutt_list_free(list);
1320}
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
@ 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:118
int mutt_any_key_to_continue(const char *s)
Prompt the user to 'press any key' and wait.
Definition curs_lib.c:175
void mutt_endwin(void)
Shutdown curses.
Definition curs_lib.c:153
int mutt_do_pager(struct PagerView *pview, struct Email *e)
Display some page-able text to the user (help or attachment)
Definition do_pager.c: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:1883
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition parse.c:1420
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition parse.c:1556
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:1257
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:1898
int mutt_body_handler(struct Body *b, struct State *state)
Handler for the Body of an email.
Definition handler.c:1664
void mutt_decode_attachment(const struct Body *b, struct State *state)
Decode an email's attachment.
Definition handler.c:1938
IMAP network mailbox.
int imap_wait_keep_alive(pid_t pid)
Wait for a process to change state.
Definition util.c:1031
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:228
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:217
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:677
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:665
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:586
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: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.
@ 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:663
#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: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: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