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