3 * Handling of email attachments
6 * Copyright (C) 1996-2000,2002,2013 Michael R. Elkins <me@mutt.org>
7 * Copyright (C) 1999-2004,2006 Thomas Roessler <roessler@does-not-exist.org>
10 * This program is free software: you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License as published by the Free Software
12 * Foundation, either version 2 of the License, or (at your option) any later
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
20 * You should have received a copy of the GNU General Public License along with
21 * this program. If not, see <http://www.gnu.org/licenses/>.
25 * @page mutt_attach Handling of email attachments
27 * Handling of email attachments
38 #include "mutt/mutt.h"
39 #include "config/lib.h"
40 #include "email/lib.h"
42 #include "mutt_attach.h"
52 #include "ncrypt/ncrypt.h"
60 * mutt_get_tmp_attachment - Get a temporary copy of an attachment
61 * @param a Attachment to copy
65 int mutt_get_tmp_attachment(struct Body *a)
73 struct Buffer *tmpfile = mutt_buffer_pool_get();
74 struct MailcapEntry *entry = mailcap_entry_new();
75 snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
76 mailcap_lookup(a, type, entry, MUTT_MC_NO_FLAGS);
77 mailcap_expand_filename(entry->nametemplate, a->filename, tmpfile);
79 mailcap_entry_free(&entry);
81 if (stat(a->filename, &st) == -1)
83 mutt_buffer_pool_release(&tmpfile);
87 FILE *fp_in = NULL, *fp_out = NULL;
88 if ((fp_in = fopen(a->filename, "r")) &&
89 (fp_out = mutt_file_fopen(mutt_b2s(tmpfile), "w")))
91 mutt_file_copy_stream(fp_in, fp_out);
92 mutt_str_replace(&a->filename, mutt_b2s(tmpfile));
95 if (a->stamp >= st.st_mtime)
96 mutt_stamp_attachment(a);
99 mutt_perror(fp_in ? mutt_b2s(tmpfile) : a->filename);
101 mutt_file_fclose(&fp_in);
102 mutt_file_fclose(&fp_out);
104 mutt_buffer_pool_release(&tmpfile);
106 return a->unlink ? 0 : -1;
110 * mutt_compose_attachment - Create an attachment
111 * @param a Body of email
112 * @retval 1 if require full screen redraw
113 * @retval 0 otherwise
115 int mutt_compose_attachment(struct Body *a)
118 struct MailcapEntry *entry = mailcap_entry_new();
119 bool unlink_newfile = false;
121 struct Buffer *cmd = mutt_buffer_pool_get();
122 struct Buffer *newfile = mutt_buffer_pool_get();
123 struct Buffer *tmpfile = mutt_buffer_pool_get();
125 snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
126 if (mailcap_lookup(a, type, entry, MUTT_MC_COMPOSE))
128 if (entry->composecommand || entry->composetypecommand)
130 if (entry->composetypecommand)
131 mutt_buffer_strcpy(cmd, entry->composetypecommand);
133 mutt_buffer_strcpy(cmd, entry->composecommand);
135 mailcap_expand_filename(entry->nametemplate, a->filename, newfile);
136 mutt_debug(LL_DEBUG1, "oldfile: %s\t newfile: %s\n", a->filename, mutt_b2s(newfile));
137 if (mutt_file_symlink(a->filename, mutt_b2s(newfile)) == -1)
139 if (mutt_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
141 mutt_buffer_strcpy(newfile, a->filename);
144 unlink_newfile = true;
146 if (mailcap_expand_command(a, mutt_b2s(newfile), type, cmd))
148 /* For now, editing requires a file, no piping */
149 mutt_error(_("Mailcap compose entry requires %%s"));
156 r = mutt_system(mutt_b2s(cmd));
158 mutt_error(_("Error running \"%s\""), mutt_b2s(cmd));
160 if ((r != -1) && entry->composetypecommand)
162 struct Body *b = NULL;
164 FILE *fp = mutt_file_fopen(a->filename, "r");
167 mutt_perror(_("Failure to open file to parse headers"));
171 b = mutt_read_mime_header(fp, 0);
174 if (!TAILQ_EMPTY(&b->parameter))
176 mutt_param_free(&a->parameter);
177 a->parameter = b->parameter;
178 TAILQ_INIT(&b->parameter);
182 FREE(&a->description);
183 a->description = b->description;
184 b->description = NULL;
189 a->form_name = b->form_name;
193 /* Remove headers by copying out data to another file, then
194 * copying the file back */
195 fseeko(fp, b->offset, SEEK_SET);
197 mutt_buffer_mktemp(tmpfile);
198 FILE *fp_tmp = mutt_file_fopen(mutt_b2s(tmpfile), "w");
201 mutt_perror(_("Failure to open file to strip headers"));
202 mutt_file_fclose(&fp);
205 mutt_file_copy_stream(fp, fp_tmp);
206 mutt_file_fclose(&fp);
207 mutt_file_fclose(&fp_tmp);
208 mutt_file_unlink(a->filename);
209 if (mutt_file_rename(mutt_b2s(tmpfile), a->filename) != 0)
211 mutt_perror(_("Failure to rename file"));
221 mutt_message(_("No mailcap compose entry for %s, creating empty file"), type);
231 unlink(mutt_b2s(newfile));
233 mutt_buffer_pool_release(&cmd);
234 mutt_buffer_pool_release(&newfile);
235 mutt_buffer_pool_release(&tmpfile);
237 mailcap_entry_free(&entry);
242 * mutt_edit_attachment - Edit an attachment
243 * @param a Email containing attachment
244 * @retval 1 if editor found
247 * Currently, this only works for send mode, as it assumes that the
248 * Body->filename actually contains the information. I'm not sure
249 * we want to deal with editing attachments we've already received,
250 * so this should be ok.
252 * Returning 0 is useful to tell the calling menu to redraw
254 int mutt_edit_attachment(struct Body *a)
257 struct MailcapEntry *entry = mailcap_entry_new();
258 bool unlink_newfile = false;
260 struct Buffer *cmd = mutt_buffer_pool_get();
261 struct Buffer *newfile = mutt_buffer_pool_get();
263 snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
264 if (mailcap_lookup(a, type, entry, MUTT_MC_EDIT))
266 if (entry->editcommand)
268 mutt_buffer_strcpy(cmd, entry->editcommand);
269 mailcap_expand_filename(entry->nametemplate, a->filename, newfile);
270 mutt_debug(LL_DEBUG1, "oldfile: %s\t newfile: %s\n", a->filename, mutt_b2s(newfile));
271 if (mutt_file_symlink(a->filename, mutt_b2s(newfile)) == -1)
273 if (mutt_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
275 mutt_buffer_strcpy(newfile, a->filename);
278 unlink_newfile = true;
280 if (mailcap_expand_command(a, mutt_b2s(newfile), type, cmd))
282 /* For now, editing requires a file, no piping */
283 mutt_error(_("Mailcap Edit entry requires %%s"));
289 if (mutt_system(mutt_b2s(cmd)) == -1)
291 mutt_error(_("Error running \"%s\""), mutt_b2s(cmd));
297 else if (a->type == TYPE_TEXT)
299 /* On text, default to editor */
300 mutt_edit_file(NONULL(C_Editor), a->filename);
304 mutt_error(_("No mailcap edit entry for %s"), type);
314 unlink(mutt_b2s(newfile));
316 mutt_buffer_pool_release(&cmd);
317 mutt_buffer_pool_release(&newfile);
319 mailcap_entry_free(&entry);
324 * mutt_check_lookup_list - Update the mime type
325 * @param b Message attachment body
326 * @param type Buffer with mime type of attachment in "type/subtype" format
327 * @param len Buffer length
329 void mutt_check_lookup_list(struct Body *b, char *type, size_t len)
331 struct ListNode *np = NULL;
332 STAILQ_FOREACH(np, &MimeLookupList, entries)
334 const int i = mutt_str_strlen(np->data) - 1;
335 if (((i > 0) && (np->data[i - 1] == '/') && (np->data[i] == '*') &&
336 (mutt_str_strncasecmp(type, np->data, i) == 0)) ||
337 (mutt_str_strcasecmp(type, np->data) == 0))
339 struct Body tmp = { 0 };
341 if ((n = mutt_lookup_mime_type(&tmp, b->filename)) != TYPE_OTHER ||
342 (n = mutt_lookup_mime_type(&tmp, b->description)) != TYPE_OTHER)
344 snprintf(type, len, "%s/%s",
347 (n == TYPE_APPLICATION) ?
351 (n == TYPE_MESSAGE) ?
355 (n == TYPE_MULTIPART) ?
357 (n == TYPE_TEXT) ? "text" : (n == TYPE_VIDEO) ? "video" : "other",
359 mutt_debug(LL_DEBUG1, "\"%s\" -> %s\n", b->filename, type);
368 * mutt_view_attachment - View an attachment
369 * @param fp Source file stream. Can be NULL
370 * @param a The message body containing the attachment
371 * @param mode How the attachment should be viewed, see #ViewAttachMode
372 * @param e Current Email. Can be NULL
373 * @param actx Attachment context
374 * @retval 0 If the viewer is run and exited successfully
376 * @retval num Return value of mutt_do_pager() when it is used
378 * Display a message attachment using the viewer program configured in mailcap.
379 * If there is no mailcap entry for a file type, view the image as text.
380 * Viewer processes are opened and waited on synchronously so viewing an
381 * attachment this way will block the main neomutt process until the viewer process
384 int mutt_view_attachment(FILE *fp, struct Body *a, enum ViewAttachMode mode,
385 struct Email *e, struct AttachCtx *actx)
387 bool use_mailcap = false;
388 bool use_pipe = false;
389 bool use_pager = true;
393 struct MailcapEntry *entry = NULL;
395 bool unlink_tempfile = false;
397 bool is_message = mutt_is_message_type(a->type, a->subtype);
398 if ((WithCrypto != 0) && is_message && a->email &&
399 (a->email->security & SEC_ENCRYPT) && !crypt_valid_passphrase(a->email->security))
404 struct Buffer *tmpfile = mutt_buffer_pool_get();
405 struct Buffer *pagerfile = mutt_buffer_pool_get();
406 struct Buffer *cmd = mutt_buffer_pool_get();
409 (mode == MUTT_VA_MAILCAP || (mode == MUTT_VA_REGULAR && mutt_needs_mailcap(a)));
410 snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
414 entry = mailcap_entry_new();
415 if (!mailcap_lookup(a, type, entry, MUTT_MC_NO_FLAGS))
417 if (mode == MUTT_VA_REGULAR)
419 /* fallback to view as text */
420 mailcap_entry_free(&entry);
421 mutt_error(_("No matching mailcap entry found. Viewing as text."));
422 mode = MUTT_VA_AS_TEXT;
434 mutt_error(_("MIME type not defined. Can't view attachment."));
437 mutt_buffer_strcpy(cmd, entry->command);
441 fname = mutt_str_strdup(a->filename);
442 mutt_file_sanitize_filename(fname, true);
447 mailcap_expand_filename(entry->nametemplate, fname, tmpfile);
448 /* send case: the file is already there; symlink to it */
451 if (mutt_file_symlink(a->filename, mutt_b2s(tmpfile)) == -1)
453 if (mutt_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
455 mutt_buffer_strcpy(tmpfile, a->filename);
458 unlink_tempfile = true;
460 /* recv case: we need to save the attachment to a file */
464 if (mutt_save_attachment(fp, a, mutt_b2s(tmpfile), MUTT_SAVE_NO_FLAGS, NULL) == -1)
466 mutt_file_chmod(mutt_b2s(tmpfile), S_IRUSR);
469 use_pipe = mailcap_expand_command(a, mutt_b2s(tmpfile), type, cmd);
470 use_pager = entry->copiousoutput;
475 if (fp && !use_mailcap && a->filename)
478 mutt_buffer_strcpy(pagerfile, a->filename);
479 mutt_adv_mktemp(pagerfile);
482 mutt_buffer_mktemp(pagerfile);
488 int fd_temp = -1, fd_pager = -1;
493 if (use_pager || use_pipe)
495 if (use_pager && ((fd_pager = mutt_file_open(mutt_b2s(pagerfile),
496 O_CREAT | O_EXCL | O_WRONLY)) == -1))
501 if (use_pipe && ((fd_temp = open(mutt_b2s(tmpfile), 0)) == -1))
509 pid = mutt_create_filter_fd(mutt_b2s(cmd), NULL, NULL, NULL,
510 use_pipe ? fd_temp : -1,
511 use_pager ? fd_pager : -1, -1);
520 mutt_error(_("Can't create filter"));
528 snprintf(desc, sizeof(desc), _("---Command: %-20.20s Description: %s"),
529 mutt_b2s(cmd), a->description);
533 snprintf(desc, sizeof(desc), _("---Command: %-30.30s Attachment: %s"),
534 mutt_b2s(cmd), type);
538 if ((mutt_wait_filter(pid) || (entry->needsterminal && C_WaitKey)) && !use_pager)
539 mutt_any_key_to_continue(NULL);
548 /* interactive cmd */
549 int rv = mutt_system(mutt_b2s(cmd));
551 mutt_debug(LL_DEBUG1, "Error running \"%s\"", cmd->data);
553 if ((rv != 0) || (entry->needsterminal && C_WaitKey))
554 mutt_any_key_to_continue(NULL);
559 /* Don't use mailcap; the attachment is viewed in the pager */
561 if (mode == MUTT_VA_AS_TEXT)
563 /* just let me see the raw data */
566 /* Viewing from a received message.
568 * Don't use mutt_save_attachment() because we want to perform charset
569 * conversion since this will be displayed by the internal pager. */
570 struct State decode_state = { 0 };
572 decode_state.fp_out = mutt_file_fopen(mutt_b2s(pagerfile), "w");
573 if (!decode_state.fp_out)
575 mutt_debug(LL_DEBUG1, "mutt_file_fopen(%s) errno=%d %s\n",
576 mutt_b2s(pagerfile), errno, strerror(errno));
577 mutt_perror(mutt_b2s(pagerfile));
580 decode_state.fp_in = fp;
581 decode_state.flags = MUTT_CHARCONV;
582 mutt_decode_attachment(a, &decode_state);
583 if (fclose(decode_state.fp_out) == EOF)
584 mutt_debug(LL_DEBUG1, "fclose(%s) errno=%d %s\n", mutt_b2s(pagerfile),
585 errno, strerror(errno));
589 /* in compose mode, just copy the file. we can't use
590 * mutt_decode_attachment() since it assumes the content-encoding has
591 * already been applied */
592 if (mutt_save_attachment(fp, a, mutt_b2s(pagerfile), MUTT_SAVE_NO_FLAGS, NULL))
598 /* Use built-in handler */
599 OptViewAttach = true; /* disable the "use 'v' to view this part"
600 * message in case of error */
601 if (mutt_decode_save_attachment(fp, a, mutt_b2s(pagerfile), MUTT_DISPLAY, MUTT_SAVE_NO_FLAGS))
603 OptViewAttach = false;
606 OptViewAttach = false;
610 mutt_str_strfcpy(desc, a->description, sizeof(desc));
611 else if (a->filename)
612 snprintf(desc, sizeof(desc), _("---Attachment: %s: %s"), a->filename, type);
614 snprintf(desc, sizeof(desc), _("---Attachment: %s"), type);
617 /* We only reach this point if there have been no errors */
621 struct Pager info = { 0 };
628 rc = mutt_do_pager(desc, mutt_b2s(pagerfile),
629 MUTT_PAGER_ATTACHMENT | (is_message ? MUTT_PAGER_MESSAGE : MUTT_PAGER_NO_FLAGS),
631 mutt_buffer_reset(pagerfile);
638 if (!entry || !entry->xneomuttkeep)
640 if (fp && !mutt_buffer_is_empty(tmpfile))
642 /* add temporary file to TempAttachmentsList to be deleted on timeout hook */
643 mutt_add_temp_attachment(mutt_b2s(tmpfile));
645 else if (unlink_tempfile)
647 unlink(mutt_b2s(tmpfile));
651 mailcap_entry_free(&entry);
653 if (!mutt_buffer_is_empty(pagerfile))
654 mutt_file_unlink(mutt_b2s(pagerfile));
656 mutt_buffer_pool_release(&tmpfile);
657 mutt_buffer_pool_release(&pagerfile);
658 mutt_buffer_pool_release(&cmd);
664 * mutt_pipe_attachment - Pipe an attachment to a command
665 * @param fp File to pipe into the command
666 * @param b Attachment
667 * @param path Path to command
668 * @param outfile File to save output to
672 int mutt_pipe_attachment(FILE *fp, struct Body *b, const char *path, char *outfile)
678 if (outfile && *outfile)
680 out = mutt_file_open(outfile, O_CREAT | O_EXCL | O_WRONLY);
694 struct State s = { 0 };
696 /* perform charset conversion on text attachments when piping */
697 s.flags = MUTT_CHARCONV;
699 if (outfile && *outfile)
700 pid = mutt_create_filter_fd(path, &s.fp_out, NULL, NULL, -1, out, -1);
702 pid = mutt_create_filter(path, &s.fp_out, NULL, NULL);
706 mutt_perror(_("Can't create filter"));
711 mutt_decode_attachment(b, &s);
712 mutt_file_fclose(&s.fp_out);
717 FILE *fp_in = fopen(b->filename, "r");
720 mutt_perror("fopen");
721 if (outfile && *outfile)
730 if (outfile && *outfile)
731 pid = mutt_create_filter_fd(path, &fp_out, NULL, NULL, -1, out, -1);
733 pid = mutt_create_filter(path, &fp_out, NULL, NULL);
737 mutt_perror(_("Can't create filter"));
738 mutt_file_fclose(&fp_in);
742 mutt_file_copy_stream(fp_in, fp_out);
743 mutt_file_fclose(&fp_out);
744 mutt_file_fclose(&fp_in);
751 if (outfile && *outfile)
754 /* check for error exit from child process */
755 if (mutt_wait_filter(pid) != 0)
758 if ((rc == 0) || C_WaitKey)
759 mutt_any_key_to_continue(NULL);
764 * save_attachment_open - Open a file to write an attachment to
765 * @param path Path to file to open
766 * @param opt Save option, see #SaveAttach
767 * @retval ptr File handle to attachment file
769 static FILE *save_attachment_open(const char *path, enum SaveAttach opt)
771 if (opt == MUTT_SAVE_APPEND)
772 return fopen(path, "a");
773 if (opt == MUTT_SAVE_OVERWRITE)
774 return fopen(path, "w");
776 return mutt_file_fopen(path, "w");
780 * mutt_save_attachment - Save an attachment
781 * @param fp Source file stream. Can be NULL
782 * @param m Email Body
783 * @param path Where to save the attachment
784 * @param opt Save option, see #SaveAttach
785 * @param e Current Email. Can be NULL
789 int mutt_save_attachment(FILE *fp, struct Body *m, const char *path,
790 enum SaveAttach opt, struct Email *e)
799 if (e && m->email && (m->encoding != ENC_BASE64) &&
800 (m->encoding != ENC_QUOTED_PRINTABLE) && mutt_is_message_type(m->type, m->subtype))
802 /* message type attachments are written to mail folders. */
805 struct Message *msg = NULL;
806 CopyHeaderFlags chflags = CH_NO_FLAGS;
809 struct Email *e_new = m->email;
810 e_new->msgno = e->msgno; /* required for MH/maildir */
813 if (fseeko(fp, m->offset, SEEK_SET) < 0)
815 if (!fgets(buf, sizeof(buf), fp))
817 struct Mailbox *m_att = mx_path_resolve(path);
818 struct Context *ctx = mx_mbox_open(m_att, MUTT_APPEND | MUTT_QUIET);
821 mailbox_free(&m_att);
824 msg = mx_msg_open_new(ctx->mailbox, e_new,
825 is_from(buf, NULL, 0, NULL) ? MUTT_MSG_NO_FLAGS : MUTT_ADD_FROM);
831 if ((ctx->mailbox->magic == MUTT_MBOX) || (ctx->mailbox->magic == MUTT_MMDF))
832 chflags = CH_FROM | CH_UPDATE_LEN;
833 chflags |= ((ctx->mailbox->magic == MUTT_MAILDIR) ? CH_NOSTATUS : CH_UPDATE);
834 if ((mutt_copy_message_fp(msg->fp, fp, e_new, MUTT_CM_NO_FLAGS, chflags) == 0) &&
835 (mx_msg_commit(ctx->mailbox, msg) == 0))
844 mx_msg_close(ctx->mailbox, &msg);
850 /* In recv mode, extract from folder and decode */
852 struct State s = { 0 };
854 s.fp_out = save_attachment_open(path, opt);
857 mutt_perror("fopen");
860 fseeko((s.fp_in = fp), m->offset, SEEK_SET);
861 mutt_decode_attachment(m, &s);
863 if (mutt_file_fsync_close(&s.fp_out) != 0)
865 mutt_perror("fclose");
875 /* In send mode, just copy file */
877 FILE *fp_old = fopen(m->filename, "r");
880 mutt_perror("fopen");
884 FILE *fp_new = save_attachment_open(path, opt);
887 mutt_perror("fopen");
888 mutt_file_fclose(&fp_old);
892 if (mutt_file_copy_stream(fp_old, fp_new) == -1)
894 mutt_error(_("Write fault"));
895 mutt_file_fclose(&fp_old);
896 mutt_file_fclose(&fp_new);
899 mutt_file_fclose(&fp_old);
900 if (mutt_file_fsync_close(&fp_new) != 0)
902 mutt_error(_("Write fault"));
911 * mutt_decode_save_attachment - Decode, then save an attachment
912 * @param fp File to read from (OPTIONAL)
913 * @param m Attachment
914 * @param path Path to save the Attachment to
915 * @param displaying Flags, e.g. #MUTT_DISPLAY
916 * @param opt Save option, see #SaveAttach
920 int mutt_decode_save_attachment(FILE *fp, struct Body *m, const char *path,
921 int displaying, enum SaveAttach opt)
923 struct State s = { 0 };
924 unsigned int saved_encoding = 0;
925 struct Body *saved_parts = NULL;
926 struct Email *e_saved = NULL;
929 s.flags = displaying;
931 if (opt == MUTT_SAVE_APPEND)
932 s.fp_out = fopen(path, "a");
933 else if (opt == MUTT_SAVE_OVERWRITE)
934 s.fp_out = fopen(path, "w");
936 s.fp_out = mutt_file_fopen(path, "w");
940 mutt_perror("fopen");
946 /* When called from the compose menu, the attachment isn't parsed,
947 * so we need to do it here. */
950 if (stat(m->filename, &st) == -1)
953 mutt_file_fclose(&s.fp_out);
957 s.fp_in = fopen(m->filename, "r");
960 mutt_perror("fopen");
964 saved_encoding = m->encoding;
965 if (!is_multipart(m))
966 m->encoding = ENC_8BIT;
968 m->length = st.st_size;
970 saved_parts = m->parts;
972 mutt_parse_part(s.fp_in, m);
974 if (m->noconv || is_multipart(m))
975 s.flags |= MUTT_CHARCONV;
980 s.flags |= MUTT_CHARCONV;
983 mutt_body_handler(m, &s);
985 if (mutt_file_fsync_close(&s.fp_out) != 0)
987 mutt_perror("fclose");
993 m->encoding = saved_encoding;
996 email_free(&m->email);
997 m->parts = saved_parts;
1000 mutt_file_fclose(&s.fp_in);
1007 * mutt_print_attachment - Print out an attachment
1008 * @param fp File to write to
1009 * @param a Attachment
1013 * Ok, the difference between send and receive:
1014 * recv: Body->filename is a suggested name, and Mailbox|Email points
1015 * to the attachment in mailbox which is encoded
1016 * send: Body->filename points to the un-encoded file which contains the
1019 int mutt_print_attachment(FILE *fp, struct Body *a)
1023 FILE *fp_in = NULL, *fp_out = NULL;
1024 bool unlink_newfile = false;
1025 struct Buffer *newfile = mutt_buffer_pool_get();
1026 struct Buffer *cmd = mutt_buffer_pool_get();
1030 snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
1032 if (mailcap_lookup(a, type, NULL, MUTT_MC_PRINT))
1036 mutt_debug(LL_DEBUG2, "Using mailcap\n");
1038 struct MailcapEntry *entry = mailcap_entry_new();
1039 mailcap_lookup(a, type, entry, MUTT_MC_PRINT);
1040 mailcap_expand_filename(entry->nametemplate, a->filename, newfile);
1041 /* send mode: symlink from existing file to the newfile */
1044 if (mutt_file_symlink(a->filename, mutt_b2s(newfile)) == -1)
1046 if (mutt_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
1047 goto mailcap_cleanup;
1048 mutt_buffer_strcpy(newfile, a->filename);
1051 unlink_newfile = true;
1053 /* in recv mode, save file to newfile first */
1056 if (mutt_save_attachment(fp, a, mutt_b2s(newfile), 0, NULL) == -1)
1057 goto mailcap_cleanup;
1060 mutt_buffer_strcpy(cmd, entry->printcommand);
1061 piped = mailcap_expand_command(a, mutt_b2s(newfile), type, cmd);
1065 /* interactive program */
1068 fp_in = fopen(mutt_b2s(newfile), "r");
1071 mutt_perror("fopen");
1072 mailcap_entry_free(&entry);
1073 goto mailcap_cleanup;
1076 pid = mutt_create_filter(mutt_b2s(cmd), &fp_out, NULL, NULL);
1079 mutt_perror(_("Can't create filter"));
1080 mailcap_entry_free(&entry);
1081 mutt_file_fclose(&fp_in);
1082 goto mailcap_cleanup;
1084 mutt_file_copy_stream(fp_in, fp_out);
1085 mutt_file_fclose(&fp_out);
1086 mutt_file_fclose(&fp_in);
1087 if (mutt_wait_filter(pid) || C_WaitKey)
1088 mutt_any_key_to_continue(NULL);
1092 int rc2 = mutt_system(mutt_b2s(cmd));
1094 mutt_debug(LL_DEBUG1, "Error running \"%s\"", cmd->data);
1096 if ((rc2 != 0) || C_WaitKey)
1097 mutt_any_key_to_continue(NULL);
1104 mutt_file_unlink(mutt_b2s(newfile));
1105 else if (unlink_newfile)
1106 unlink(mutt_b2s(newfile));
1108 mailcap_entry_free(&entry);
1112 if ((mutt_str_strcasecmp("text/plain", type) == 0) ||
1113 (mutt_str_strcasecmp("application/postscript", type) == 0))
1115 rc = (mutt_pipe_attachment(fp, a, NONULL(C_PrintCommand), NULL));
1118 else if (mutt_can_decode(a))
1120 /* decode and print */
1125 mutt_buffer_mktemp(newfile);
1126 if (mutt_decode_save_attachment(fp, a, mutt_b2s(newfile), MUTT_PRINTING, 0) == 0)
1128 mutt_debug(LL_DEBUG2, "successfully decoded %s type attachment to %s\n",
1129 type, mutt_b2s(newfile));
1131 fp_in = fopen(mutt_b2s(newfile), "r");
1134 mutt_perror("fopen");
1135 goto decode_cleanup;
1138 mutt_debug(LL_DEBUG2, "successfully opened %s read-only\n", mutt_b2s(newfile));
1141 pid = mutt_create_filter(NONULL(C_PrintCommand), &fp_out, NULL, NULL);
1144 mutt_perror(_("Can't create filter"));
1145 goto decode_cleanup;
1148 mutt_debug(LL_DEBUG2, "Filter created\n");
1150 mutt_file_copy_stream(fp_in, fp_out);
1152 mutt_file_fclose(&fp_out);
1153 mutt_file_fclose(&fp_in);
1155 if ((mutt_wait_filter(pid) != 0) || C_WaitKey)
1156 mutt_any_key_to_continue(NULL);
1160 mutt_file_fclose(&fp_in);
1161 mutt_file_fclose(&fp_out);
1162 mutt_file_unlink(mutt_b2s(newfile));
1166 mutt_error(_("I don't know how to print that"));
1171 mutt_buffer_pool_release(&newfile);
1172 mutt_buffer_pool_release(&cmd);
1178 * mutt_add_temp_attachment - Add file to list of temporary attachments
1179 * @param filename filename with full path
1181 void mutt_add_temp_attachment(const char *filename)
1183 mutt_list_insert_tail(&TempAttachmentsList, mutt_str_strdup(filename));
1187 * mutt_unlink_temp_attachments - Delete all temporary attachments
1189 void mutt_unlink_temp_attachments(void)
1191 struct ListNode *np = NULL;
1193 STAILQ_FOREACH(np, &TempAttachmentsList, entries)
1195 mutt_file_chmod_add(np->data, S_IWUSR);
1196 mutt_file_unlink(np->data);
1199 mutt_list_free(&TempAttachmentsList);