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