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