mutt stable branch with some hacks
1/*
2 * Copyright (C) 1996-2000,2002,2013 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 1999-2004,2006 Thomas Roessler <roessler@does-not-exist.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20#if HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include "mutt.h"
25#include "mutt_menu.h"
26#include "attach.h"
27#include "mutt_curses.h"
28#include "keymap.h"
29#include "rfc1524.h"
30#include "mime.h"
31#include "pager.h"
32#include "mailbox.h"
33#include "copy.h"
34#include "mx.h"
35#include "mutt_crypt.h"
36
37#include <ctype.h>
38#include <stdlib.h>
39#include <unistd.h>
40#include <sys/wait.h>
41#include <sys/stat.h>
42#include <fcntl.h>
43#include <string.h>
44#include <errno.h>
45
46int mutt_get_tmp_attachment (BODY *a)
47{
48 char type[STRING];
49 char tempfile[_POSIX_PATH_MAX];
50 rfc1524_entry *entry = rfc1524_new_entry();
51 FILE *fpin = NULL, *fpout = NULL;
52 struct stat st;
53
54 if(a->unlink)
55 return 0;
56
57 snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
58 rfc1524_mailcap_lookup(a, type, entry, 0);
59 rfc1524_expand_filename(entry->nametemplate, a->filename,
60 tempfile, sizeof(tempfile));
61
62 rfc1524_free_entry(&entry);
63
64 if(stat(a->filename, &st) == -1)
65 return -1;
66
67 if((fpin = fopen(a->filename, "r")) && (fpout = safe_fopen(tempfile, "w"))) /* __FOPEN_CHECKED__ */
68 {
69 mutt_copy_stream (fpin, fpout);
70 mutt_str_replace (&a->filename, tempfile);
71 a->unlink = 1;
72
73 if(a->stamp >= st.st_mtime)
74 mutt_stamp_attachment(a);
75 }
76 else
77 mutt_perror(fpin ? tempfile : a->filename);
78
79 if(fpin) safe_fclose (&fpin);
80 if(fpout) safe_fclose (&fpout);
81
82 return a->unlink ? 0 : -1;
83}
84
85
86/* return 1 if require full screen redraw, 0 otherwise */
87int mutt_compose_attachment (BODY *a)
88{
89 char type[STRING];
90 char command[STRING];
91 char newfile[_POSIX_PATH_MAX] = "";
92 rfc1524_entry *entry = rfc1524_new_entry ();
93 short unlink_newfile = 0;
94 int rc = 0;
95
96 snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
97 if (rfc1524_mailcap_lookup (a, type, entry, MUTT_COMPOSE))
98 {
99 if (entry->composecommand || entry->composetypecommand)
100 {
101
102 if (entry->composetypecommand)
103 strfcpy (command, entry->composetypecommand, sizeof (command));
104 else
105 strfcpy (command, entry->composecommand, sizeof (command));
106 if (rfc1524_expand_filename (entry->nametemplate,
107 a->filename, newfile, sizeof (newfile)))
108 {
109 dprint(1, (debugfile, "oldfile: %s\t newfile: %s\n",
110 a->filename, newfile));
111 if (safe_symlink (a->filename, newfile) == -1)
112 {
113 if (mutt_yesorno (_("Can't match nametemplate, continue?"), MUTT_YES) != MUTT_YES)
114 goto bailout;
115 }
116 else
117 unlink_newfile = 1;
118 }
119 else
120 strfcpy(newfile, a->filename, sizeof(newfile));
121
122 if (rfc1524_expand_command (a, newfile, type,
123 command, sizeof (command)))
124 {
125 /* For now, editing requires a file, no piping */
126 mutt_error _("Mailcap compose entry requires %%s");
127 }
128 else
129 {
130 int r;
131
132 mutt_endwin (NULL);
133 if ((r = mutt_system (command)) == -1)
134 mutt_error (_("Error running \"%s\"!"), command);
135
136 if (r != -1 && entry->composetypecommand)
137 {
138 BODY *b;
139 FILE *fp, *tfp;
140 char tempfile[_POSIX_PATH_MAX];
141
142 if ((fp = safe_fopen (a->filename, "r")) == NULL)
143 {
144 mutt_perror _("Failure to open file to parse headers.");
145 goto bailout;
146 }
147
148 b = mutt_read_mime_header (fp, 0);
149 if (b)
150 {
151 if (b->parameter)
152 {
153 mutt_free_parameter (&a->parameter);
154 a->parameter = b->parameter;
155 b->parameter = NULL;
156 }
157 if (b->description) {
158 FREE (&a->description);
159 a->description = b->description;
160 b->description = NULL;
161 }
162 if (b->form_name)
163 {
164 FREE (&a->form_name);
165 a->form_name = b->form_name;
166 b->form_name = NULL;
167 }
168
169 /* Remove headers by copying out data to another file, then
170 * copying the file back */
171 fseeko (fp, b->offset, 0);
172 mutt_mktemp (tempfile, sizeof (tempfile));
173 if ((tfp = safe_fopen (tempfile, "w")) == NULL)
174 {
175 mutt_perror _("Failure to open file to strip headers.");
176 goto bailout;
177 }
178 mutt_copy_stream (fp, tfp);
179 safe_fclose (&fp);
180 safe_fclose (&tfp);
181 mutt_unlink (a->filename);
182 if (mutt_rename_file (tempfile, a->filename) != 0)
183 {
184 mutt_perror _("Failure to rename file.");
185 goto bailout;
186 }
187
188 mutt_free_body (&b);
189 }
190 }
191 }
192 }
193 }
194 else
195 {
196 rfc1524_free_entry (&entry);
197 mutt_message (_("No mailcap compose entry for %s, creating empty file."),
198 type);
199 return 1;
200 }
201
202 rc = 1;
203
204 bailout:
205
206 if(unlink_newfile)
207 unlink(newfile);
208
209 rfc1524_free_entry (&entry);
210 return rc;
211}
212
213/*
214 * Currently, this only works for send mode, as it assumes that the
215 * BODY->filename actually contains the information. I'm not sure
216 * we want to deal with editing attachments we've already received,
217 * so this should be ok.
218 *
219 * Returns 1 if editor found, 0 if not (useful to tell calling menu to
220 * redraw)
221 */
222int mutt_edit_attachment (BODY *a)
223{
224 char type[STRING];
225 char command[STRING];
226 char newfile[_POSIX_PATH_MAX] = "";
227 rfc1524_entry *entry = rfc1524_new_entry ();
228 short unlink_newfile = 0;
229 int rc = 0;
230
231 snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
232 if (rfc1524_mailcap_lookup (a, type, entry, MUTT_EDIT))
233 {
234 if (entry->editcommand)
235 {
236
237 strfcpy (command, entry->editcommand, sizeof (command));
238 if (rfc1524_expand_filename (entry->nametemplate,
239 a->filename, newfile, sizeof (newfile)))
240 {
241 dprint(1, (debugfile, "oldfile: %s\t newfile: %s\n",
242 a->filename, newfile));
243 if (safe_symlink (a->filename, newfile) == -1)
244 {
245 if (mutt_yesorno (_("Can't match nametemplate, continue?"), MUTT_YES) != MUTT_YES)
246 goto bailout;
247 }
248 else
249 unlink_newfile = 1;
250 }
251 else
252 strfcpy(newfile, a->filename, sizeof(newfile));
253
254 if (rfc1524_expand_command (a, newfile, type,
255 command, sizeof (command)))
256 {
257 /* For now, editing requires a file, no piping */
258 mutt_error _("Mailcap Edit entry requires %%s");
259 goto bailout;
260 }
261 else
262 {
263 mutt_endwin (NULL);
264 if (mutt_system (command) == -1)
265 {
266 mutt_error (_("Error running \"%s\"!"), command);
267 goto bailout;
268 }
269 }
270 }
271 }
272 else if (a->type == TYPETEXT)
273 {
274 /* On text, default to editor */
275 mutt_edit_file (NONULL (Editor), a->filename);
276 }
277 else
278 {
279 rfc1524_free_entry (&entry);
280 mutt_error (_("No mailcap edit entry for %s"),type);
281 return 0;
282 }
283
284 rc = 1;
285
286 bailout:
287
288 if(unlink_newfile)
289 unlink(newfile);
290
291 rfc1524_free_entry (&entry);
292 return rc;
293}
294
295
296void mutt_check_lookup_list (BODY *b, char *type, int len)
297{
298 LIST *t = MimeLookupList;
299 int i;
300
301 for (; t; t = t->next) {
302 i = mutt_strlen (t->data) - 1;
303 if ((i > 0 && t->data[i-1] == '/' && t->data[i] == '*' &&
304 ascii_strncasecmp (type, t->data, i) == 0) ||
305 ascii_strcasecmp (type, t->data) == 0) {
306
307 BODY tmp = {0};
308 int n;
309 if ((n = mutt_lookup_mime_type (&tmp, b->filename)) != TYPEOTHER) {
310 snprintf (type, len, "%s/%s",
311 n == TYPEAUDIO ? "audio" :
312 n == TYPEAPPLICATION ? "application" :
313 n == TYPEIMAGE ? "image" :
314 n == TYPEMESSAGE ? "message" :
315 n == TYPEMODEL ? "model" :
316 n == TYPEMULTIPART ? "multipart" :
317 n == TYPETEXT ? "text" :
318 n == TYPEVIDEO ? "video" : "other",
319 tmp.subtype);
320 dprint(1, (debugfile, "mutt_check_lookup_list: \"%s\" -> %s\n",
321 b->filename, type));
322 }
323 if (tmp.subtype)
324 FREE (&tmp.subtype);
325 if (tmp.xtype)
326 FREE (&tmp.xtype);
327 }
328 }
329}
330
331/* returns -1 on error, 0 or the return code from mutt_do_pager() on success */
332int mutt_view_attachment (FILE *fp, BODY *a, int flag, HEADER *hdr,
333 ATTACHPTR **idx, short idxlen)
334{
335 char tempfile[_POSIX_PATH_MAX] = "";
336 char pagerfile[_POSIX_PATH_MAX] = "";
337 int is_message;
338 int use_mailcap;
339 int use_pipe = 0;
340 int use_pager = 1;
341 char type[STRING];
342 char command[HUGE_STRING];
343 char descrip[STRING];
344 char *fname;
345 rfc1524_entry *entry = NULL;
346 int rc = -1;
347 int unlink_tempfile = 0;
348
349 is_message = mutt_is_message_type(a->type, a->subtype);
350 if (WithCrypto && is_message && a->hdr && (a->hdr->security & ENCRYPT) &&
351 !crypt_valid_passphrase(a->hdr->security))
352 return (rc);
353 use_mailcap = (flag == MUTT_MAILCAP ||
354 (flag == MUTT_REGULAR && mutt_needs_mailcap (a)));
355 snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
356
357 if (use_mailcap)
358 {
359 entry = rfc1524_new_entry ();
360 if (!rfc1524_mailcap_lookup (a, type, entry, 0))
361 {
362 if (flag == MUTT_REGULAR)
363 {
364 /* fallback to view as text */
365 rfc1524_free_entry (&entry);
366 mutt_error _("No matching mailcap entry found. Viewing as text.");
367 flag = MUTT_AS_TEXT;
368 use_mailcap = 0;
369 }
370 else
371 goto return_error;
372 }
373 }
374
375 if (use_mailcap)
376 {
377 if (!entry->command)
378 {
379 mutt_error _("MIME type not defined. Cannot view attachment.");
380 goto return_error;
381 }
382 strfcpy (command, entry->command, sizeof (command));
383
384 if (fp)
385 {
386 fname = safe_strdup (a->filename);
387 mutt_sanitize_filename (fname, 1);
388 }
389 else
390 fname = a->filename;
391
392 if (rfc1524_expand_filename (entry->nametemplate, fname,
393 tempfile, sizeof (tempfile)))
394 {
395 if (fp == NULL && mutt_strcmp(tempfile, a->filename))
396 {
397 /* send case: the file is already there */
398 if (safe_symlink (a->filename, tempfile) == -1)
399 {
400 if (mutt_yesorno (_("Can't match nametemplate, continue?"), MUTT_YES) == MUTT_YES)
401 strfcpy (tempfile, a->filename, sizeof (tempfile));
402 else
403 goto return_error;
404 }
405 else
406 unlink_tempfile = 1;
407 }
408 }
409 else if (fp == NULL) /* send case */
410 strfcpy (tempfile, a->filename, sizeof (tempfile));
411
412 if (fp)
413 {
414 /* recv case: we need to save the attachment to a file */
415 FREE (&fname);
416 if (mutt_save_attachment (fp, a, tempfile, 0, NULL) == -1)
417 goto return_error;
418 }
419
420 use_pipe = rfc1524_expand_command (a, tempfile, type,
421 command, sizeof (command));
422 use_pager = entry->copiousoutput;
423 }
424
425 if (use_pager)
426 {
427 if (fp && !use_mailcap && a->filename)
428 {
429 /* recv case */
430 strfcpy (pagerfile, a->filename, sizeof (pagerfile));
431 mutt_adv_mktemp (pagerfile, sizeof(pagerfile));
432 }
433 else
434 mutt_mktemp (pagerfile, sizeof (pagerfile));
435 }
436
437 if (use_mailcap)
438 {
439 pid_t thepid = 0;
440 int tempfd = -1, pagerfd = -1;
441
442 if (!use_pager)
443 mutt_endwin (NULL);
444
445 if (use_pager || use_pipe)
446 {
447 if (use_pager && ((pagerfd = safe_open (pagerfile, O_CREAT | O_EXCL | O_WRONLY)) == -1))
448 {
449 mutt_perror ("open");
450 goto return_error;
451 }
452 if (use_pipe && ((tempfd = open (tempfile, 0)) == -1))
453 {
454 if(pagerfd != -1)
455 close(pagerfd);
456 mutt_perror ("open");
457 goto return_error;
458 }
459
460 if ((thepid = mutt_create_filter_fd (command, NULL, NULL, NULL,
461 use_pipe ? tempfd : -1, use_pager ? pagerfd : -1, -1)) == -1)
462 {
463 if(pagerfd != -1)
464 close(pagerfd);
465
466 if(tempfd != -1)
467 close(tempfd);
468
469 mutt_error _("Cannot create filter");
470 goto return_error;
471 }
472
473 if (use_pager)
474 {
475 if (a->description)
476 snprintf (descrip, sizeof (descrip),
477 _("---Command: %-20.20s Description: %s"),
478 command, a->description);
479 else
480 snprintf (descrip, sizeof (descrip),
481 _("---Command: %-30.30s Attachment: %s"), command, type);
482 }
483
484 if ((mutt_wait_filter (thepid) || (entry->needsterminal &&
485 option (OPTWAITKEY))) && !use_pager)
486 mutt_any_key_to_continue (NULL);
487
488 if (tempfd != -1)
489 close (tempfd);
490 if (pagerfd != -1)
491 close (pagerfd);
492 }
493 else
494 {
495 /* interactive command */
496 if (mutt_system (command) ||
497 (entry->needsterminal && option (OPTWAITKEY)))
498 mutt_any_key_to_continue (NULL);
499 }
500 }
501 else
502 {
503 /* Don't use mailcap; the attachment is viewed in the pager */
504
505 if (flag == MUTT_AS_TEXT)
506 {
507 /* just let me see the raw data */
508 if (fp)
509 {
510 /* Viewing from a received message.
511 *
512 * Don't use mutt_save_attachment() because we want to perform charset
513 * conversion since this will be displayed by the internal pager.
514 */
515 STATE decode_state;
516
517 memset(&decode_state, 0, sizeof(decode_state));
518 decode_state.fpout = safe_fopen(pagerfile, "w");
519 if (!decode_state.fpout)
520 {
521 dprint(1, (debugfile, "mutt_view_attachment:%d safe_fopen(%s) errno=%d %s\n", __LINE__, pagerfile, errno, strerror(errno)));
522 mutt_perror(pagerfile);
523 mutt_sleep(1);
524 goto return_error;
525 }
526 decode_state.fpin = fp;
527 decode_state.flags = MUTT_CHARCONV;
528 mutt_decode_attachment(a, &decode_state);
529 if (fclose(decode_state.fpout) == EOF)
530 dprint(1, (debugfile, "mutt_view_attachment:%d fclose errno=%d %s\n", __LINE__, pagerfile, errno, strerror(errno)));
531 }
532 else
533 {
534 /* in compose mode, just copy the file. we can't use
535 * mutt_decode_attachment() since it assumes the content-encoding has
536 * already been applied
537 */
538 if (mutt_save_attachment(fp, a, pagerfile, 0, NULL))
539 goto return_error;
540 }
541 }
542 else
543 {
544 /* Use built-in handler */
545 set_option (OPTVIEWATTACH); /* disable the "use 'v' to view this part"
546 * message in case of error */
547 if (mutt_decode_save_attachment (fp, a, pagerfile, MUTT_DISPLAY, 0))
548 {
549 unset_option (OPTVIEWATTACH);
550 goto return_error;
551 }
552 unset_option (OPTVIEWATTACH);
553 }
554
555 if (a->description)
556 strfcpy (descrip, a->description, sizeof (descrip));
557 else if (a->filename)
558 snprintf (descrip, sizeof (descrip), _("---Attachment: %s: %s"),
559 a->filename, type);
560 else
561 snprintf (descrip, sizeof (descrip), _("---Attachment: %s"), type);
562 }
563
564 /* We only reach this point if there have been no errors */
565
566 if (use_pager)
567 {
568 pager_t info;
569
570 memset (&info, 0, sizeof (info));
571 info.fp = fp;
572 info.bdy = a;
573 info.ctx = Context;
574 info.idx = idx;
575 info.idxlen = idxlen;
576 info.hdr = hdr;
577
578 rc = mutt_do_pager (descrip, pagerfile,
579 MUTT_PAGER_ATTACHMENT | (is_message ? MUTT_PAGER_MESSAGE : 0), &info);
580 *pagerfile = '\0';
581 }
582 else
583 rc = 0;
584
585 return_error:
586
587 if (entry)
588 rfc1524_free_entry (&entry);
589 if (fp && tempfile[0])
590 mutt_unlink (tempfile);
591 else if (unlink_tempfile)
592 unlink(tempfile);
593
594 if (pagerfile[0])
595 mutt_unlink (pagerfile);
596
597 return rc;
598}
599
600/* returns 1 on success, 0 on error */
601int mutt_pipe_attachment (FILE *fp, BODY *b, const char *path, char *outfile)
602{
603 pid_t thepid;
604 int out = -1;
605 int rv = 0;
606
607 if (outfile && *outfile)
608 if ((out = safe_open (outfile, O_CREAT | O_EXCL | O_WRONLY)) < 0)
609 {
610 mutt_perror ("open");
611 return 0;
612 }
613
614 mutt_endwin (NULL);
615
616 if (fp)
617 {
618 /* recv case */
619
620 STATE s;
621
622 memset (&s, 0, sizeof (STATE));
623 /* perform charset conversion on text attachments when piping */
624 s.flags = MUTT_CHARCONV;
625
626 if (outfile && *outfile)
627 thepid = mutt_create_filter_fd (path, &s.fpout, NULL, NULL, -1, out, -1);
628 else
629 thepid = mutt_create_filter (path, &s.fpout, NULL, NULL);
630
631 if (thepid < 0)
632 {
633 mutt_perror _("Can't create filter");
634 goto bail;
635 }
636
637 s.fpin = fp;
638 mutt_decode_attachment (b, &s);
639 safe_fclose (&s.fpout);
640 }
641 else
642 {
643 /* send case */
644
645 FILE *ifp, *ofp;
646
647 if ((ifp = fopen (b->filename, "r")) == NULL)
648 {
649 mutt_perror ("fopen");
650 if (outfile && *outfile)
651 {
652 close (out);
653 unlink (outfile);
654 }
655 return 0;
656 }
657
658 if (outfile && *outfile)
659 thepid = mutt_create_filter_fd (path, &ofp, NULL, NULL, -1, out, -1);
660 else
661 thepid = mutt_create_filter (path, &ofp, NULL, NULL);
662
663 if (thepid < 0)
664 {
665 mutt_perror _("Can't create filter");
666 safe_fclose (&ifp);
667 goto bail;
668 }
669
670 mutt_copy_stream (ifp, ofp);
671 safe_fclose (&ofp);
672 safe_fclose (&ifp);
673 }
674
675 rv = 1;
676
677bail:
678
679 if (outfile && *outfile)
680 close (out);
681
682 /*
683 * check for error exit from child process
684 */
685 if (mutt_wait_filter (thepid) != 0)
686 rv = 0;
687
688 if (rv == 0 || option (OPTWAITKEY))
689 mutt_any_key_to_continue (NULL);
690 return rv;
691}
692
693static FILE *
694mutt_save_attachment_open (char *path, int flags)
695{
696 if (flags == MUTT_SAVE_APPEND)
697 return fopen (path, "a");
698 if (flags == MUTT_SAVE_OVERWRITE)
699 return fopen (path, "w"); /* __FOPEN_CHECKED__ */
700
701 return safe_fopen (path, "w");
702}
703
704/* returns 0 on success, -1 on error */
705int mutt_save_attachment (FILE *fp, BODY *m, char *path, int flags, HEADER *hdr)
706{
707 if (fp)
708 {
709
710 /* recv mode */
711
712 if(hdr &&
713 m->hdr &&
714 m->encoding != ENCBASE64 &&
715 m->encoding != ENCQUOTEDPRINTABLE &&
716 mutt_is_message_type(m->type, m->subtype))
717 {
718 /* message type attachments are written to mail folders. */
719
720 char buf[HUGE_STRING];
721 HEADER *hn;
722 CONTEXT ctx;
723 MESSAGE *msg;
724 int chflags = 0;
725 int r = -1;
726
727 hn = m->hdr;
728 hn->msgno = hdr->msgno; /* required for MH/maildir */
729 hn->read = 1;
730
731 fseeko (fp, m->offset, 0);
732 if (fgets (buf, sizeof (buf), fp) == NULL)
733 return -1;
734 if (mx_open_mailbox(path, MUTT_APPEND | MUTT_QUIET, &ctx) == NULL)
735 return -1;
736 if ((msg = mx_open_new_message (&ctx, hn, is_from (buf, NULL, 0, NULL) ? 0 : MUTT_ADD_FROM)) == NULL)
737 {
738 mx_close_mailbox(&ctx, NULL);
739 return -1;
740 }
741 if (ctx.magic == MUTT_MBOX || ctx.magic == MUTT_MMDF)
742 chflags = CH_FROM | CH_UPDATE_LEN;
743 chflags |= (ctx.magic == MUTT_MAILDIR ? CH_NOSTATUS : CH_UPDATE);
744 if (_mutt_copy_message (msg->fp, fp, hn, hn->content, 0, chflags) == 0
745 && mx_commit_message (msg, &ctx) == 0)
746 r = 0;
747 else
748 r = -1;
749
750 mx_close_message (&ctx, &msg);
751 mx_close_mailbox (&ctx, NULL);
752 return r;
753 }
754 else
755 {
756 /* In recv mode, extract from folder and decode */
757
758 STATE s;
759
760 memset (&s, 0, sizeof (s));
761 if ((s.fpout = mutt_save_attachment_open (path, flags)) == NULL)
762 {
763 mutt_perror ("fopen");
764 mutt_sleep (2);
765 return (-1);
766 }
767 fseeko ((s.fpin = fp), m->offset, 0);
768 mutt_decode_attachment (m, &s);
769
770 if (fclose (s.fpout) != 0)
771 {
772 mutt_perror ("fclose");
773 mutt_sleep (2);
774 return (-1);
775 }
776 }
777 }
778 else
779 {
780 /* In send mode, just copy file */
781
782 FILE *ofp, *nfp;
783
784 if ((ofp = fopen (m->filename, "r")) == NULL)
785 {
786 mutt_perror ("fopen");
787 return (-1);
788 }
789
790 if ((nfp = mutt_save_attachment_open (path, flags)) == NULL)
791 {
792 mutt_perror ("fopen");
793 safe_fclose (&ofp);
794 return (-1);
795 }
796
797 if (mutt_copy_stream (ofp, nfp) == -1)
798 {
799 mutt_error _("Write fault!");
800 safe_fclose (&ofp);
801 safe_fclose (&nfp);
802 return (-1);
803 }
804 safe_fclose (&ofp);
805 safe_fclose (&nfp);
806 }
807
808 return 0;
809}
810
811/* returns 0 on success, -1 on error */
812int mutt_decode_save_attachment (FILE *fp, BODY *m, char *path,
813 int displaying, int flags)
814{
815 STATE s;
816 unsigned int saved_encoding = 0;
817 BODY *saved_parts = NULL;
818 HEADER *saved_hdr = NULL;
819
820 memset (&s, 0, sizeof (s));
821 s.flags = displaying;
822
823 if (flags == MUTT_SAVE_APPEND)
824 s.fpout = fopen (path, "a");
825 else if (flags == MUTT_SAVE_OVERWRITE)
826 s.fpout = fopen (path, "w"); /* __FOPEN_CHECKED__ */
827 else
828 s.fpout = safe_fopen (path, "w");
829
830 if (s.fpout == NULL)
831 {
832 mutt_perror ("fopen");
833 return (-1);
834 }
835
836 if (fp == NULL)
837 {
838 /* When called from the compose menu, the attachment isn't parsed,
839 * so we need to do it here. */
840 struct stat st;
841
842 if (stat (m->filename, &st) == -1)
843 {
844 mutt_perror ("stat");
845 safe_fclose (&s.fpout);
846 return (-1);
847 }
848
849 if ((s.fpin = fopen (m->filename, "r")) == NULL)
850 {
851 mutt_perror ("fopen");
852 return (-1);
853 }
854
855 saved_encoding = m->encoding;
856 if (!is_multipart (m))
857 m->encoding = ENC8BIT;
858
859 m->length = st.st_size;
860 m->offset = 0;
861 saved_parts = m->parts;
862 saved_hdr = m->hdr;
863 mutt_parse_part (s.fpin, m);
864
865 if (m->noconv || is_multipart (m))
866 s.flags |= MUTT_CHARCONV;
867 }
868 else
869 {
870 s.fpin = fp;
871 s.flags |= MUTT_CHARCONV;
872 }
873
874 mutt_body_handler (m, &s);
875
876 safe_fclose (&s.fpout);
877 if (fp == NULL)
878 {
879 m->length = 0;
880 m->encoding = saved_encoding;
881 if (saved_parts)
882 {
883 mutt_free_header (&m->hdr);
884 m->parts = saved_parts;
885 m->hdr = saved_hdr;
886 }
887 safe_fclose (&s.fpin);
888 }
889
890 return (0);
891}
892
893/* Ok, the difference between send and receive:
894 * recv: BODY->filename is a suggested name, and Context|HEADER points
895 * to the attachment in mailbox which is encoded
896 * send: BODY->filename points to the un-encoded file which contains the
897 * attachment
898 */
899
900int mutt_print_attachment (FILE *fp, BODY *a)
901{
902 char newfile[_POSIX_PATH_MAX] = "";
903 char type[STRING];
904 pid_t thepid;
905 FILE *ifp, *fpout;
906 short unlink_newfile = 0;
907
908 snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
909
910 if (rfc1524_mailcap_lookup (a, type, NULL, MUTT_PRINT))
911 {
912 char command[_POSIX_PATH_MAX+STRING];
913 rfc1524_entry *entry;
914 int piped = FALSE;
915
916 dprint (2, (debugfile, "Using mailcap...\n"));
917
918 entry = rfc1524_new_entry ();
919 rfc1524_mailcap_lookup (a, type, entry, MUTT_PRINT);
920 if (rfc1524_expand_filename (entry->nametemplate, a->filename,
921 newfile, sizeof (newfile)))
922 {
923 if (!fp)
924 {
925 if (safe_symlink(a->filename, newfile) == -1)
926 {
927 if (mutt_yesorno (_("Can't match nametemplate, continue?"), MUTT_YES) != MUTT_YES)
928 {
929 rfc1524_free_entry (&entry);
930 return 0;
931 }
932 strfcpy (newfile, a->filename, sizeof (newfile));
933 }
934 else
935 unlink_newfile = 1;
936 }
937 }
938
939 /* in recv mode, save file to newfile first */
940 if (fp)
941 mutt_save_attachment (fp, a, newfile, 0, NULL);
942
943 strfcpy (command, entry->printcommand, sizeof (command));
944 piped = rfc1524_expand_command (a, newfile, type, command, sizeof (command));
945
946 mutt_endwin (NULL);
947
948 /* interactive program */
949 if (piped)
950 {
951 if ((ifp = fopen (newfile, "r")) == NULL)
952 {
953 mutt_perror ("fopen");
954 rfc1524_free_entry (&entry);
955 return (0);
956 }
957
958 if ((thepid = mutt_create_filter (command, &fpout, NULL, NULL)) < 0)
959 {
960 mutt_perror _("Can't create filter");
961 rfc1524_free_entry (&entry);
962 safe_fclose (&ifp);
963 return 0;
964 }
965 mutt_copy_stream (ifp, fpout);
966 safe_fclose (&fpout);
967 safe_fclose (&ifp);
968 if (mutt_wait_filter (thepid) || option (OPTWAITKEY))
969 mutt_any_key_to_continue (NULL);
970 }
971 else
972 {
973 if (mutt_system (command) || option (OPTWAITKEY))
974 mutt_any_key_to_continue (NULL);
975 }
976
977 if (fp)
978 mutt_unlink (newfile);
979 else if (unlink_newfile)
980 unlink(newfile);
981
982 rfc1524_free_entry (&entry);
983 return (1);
984 }
985
986 if (!ascii_strcasecmp ("text/plain", type) ||
987 !ascii_strcasecmp ("application/postscript", type))
988 {
989 return (mutt_pipe_attachment (fp, a, NONULL(PrintCmd), NULL));
990 }
991 else if (mutt_can_decode (a))
992 {
993 /* decode and print */
994
995 int rc = 0;
996
997 ifp = NULL;
998 fpout = NULL;
999
1000 mutt_mktemp (newfile, sizeof (newfile));
1001 if (mutt_decode_save_attachment (fp, a, newfile, MUTT_PRINTING, 0) == 0)
1002 {
1003
1004 dprint (2, (debugfile, "successfully decoded %s type attachment to %s\n",
1005 type, newfile));
1006
1007 if ((ifp = fopen (newfile, "r")) == NULL)
1008 {
1009 mutt_perror ("fopen");
1010 goto bail0;
1011 }
1012
1013 dprint (2, (debugfile, "successfully opened %s read-only\n", newfile));
1014
1015 mutt_endwin (NULL);
1016 if ((thepid = mutt_create_filter (NONULL(PrintCmd), &fpout, NULL, NULL)) < 0)
1017 {
1018 mutt_perror _("Can't create filter");
1019 goto bail0;
1020 }
1021
1022 dprint (2, (debugfile, "Filter created.\n"));
1023
1024 mutt_copy_stream (ifp, fpout);
1025
1026 safe_fclose (&fpout);
1027 safe_fclose (&ifp);
1028
1029 if (mutt_wait_filter (thepid) != 0 || option (OPTWAITKEY))
1030 mutt_any_key_to_continue (NULL);
1031 rc = 1;
1032 }
1033 bail0:
1034 safe_fclose (&ifp);
1035 safe_fclose (&fpout);
1036 mutt_unlink (newfile);
1037 return rc;
1038 }
1039 else
1040 {
1041 mutt_error _("I don't know how to print that!");
1042 return 0;
1043 }
1044}