Better error msg in gpgme
[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   int pagerfd = -1;
377
378   is_message = mutt_is_message_type (a->type, a->subtype);
379   if (is_message && a->hdr && (a->hdr->security & ENCRYPT) &&
380       !crypt_valid_passphrase (a->hdr->security))
381     return (rc);
382   use_mailcap = (flag == M_MAILCAP ||
383                  (flag == M_REGULAR && rfc1524_mailcap_isneeded(a)));
384   snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
385
386   if (use_mailcap) {
387     entry = rfc1524_entry_new();
388     if (!rfc1524_mailcap_lookup (a, type, entry, 0)) {
389       if (flag == M_REGULAR) {
390         /* fallback to view as text */
391         rfc1524_entry_delete(&entry);
392         mutt_error _("No matching mailcap entry found.  Viewing as text.");
393
394         flag = M_AS_TEXT;
395         use_mailcap = 0;
396       }
397       else
398         goto return_error;
399     }
400   }
401
402   if (use_mailcap) {
403     if (!entry->command) {
404       mutt_error _("MIME type not defined.  Cannot view attachment.");
405
406       goto return_error;
407     }
408     m_strcpy(command, sizeof(command), entry->command);
409
410     if (fp) {
411       fname = m_strdup(a->filename);
412       mutt_sanitize_filename (fname, 1);
413     }
414     else
415       fname = a->filename;
416
417     if (rfc1524_expand_filename (entry->nametemplate, fname,
418                                  tempfile, sizeof (tempfile))) {
419       if (fp == NULL && m_strcmp(tempfile, a->filename)) {
420         /* send case: the file is already there */
421         if (safe_symlink (a->filename, tempfile) == -1) {
422           if (mutt_yesorno (_("Can't match nametemplate, continue?"), M_YES)
423               == M_YES)
424             m_strcpy(tempfile, sizeof(tempfile), a->filename);
425           else
426             goto return_error;
427         }
428         else
429           unlink_tempfile = 1;
430       }
431     }
432     else if (fp == NULL)        /* send case */
433       m_strcpy(tempfile, sizeof(tempfile), a->filename);
434
435     if (fp) {
436       /* recv case: we need to save the attachment to a file */
437       p_delete(&fname);
438       if (mutt_save_attachment (fp, a, tempfile, 0, NULL) == -1)
439         goto return_error;
440     }
441
442     use_pipe = rfc1524_expand_command (a, tempfile, type,
443                                        command, sizeof (command));
444     use_pager = entry->copiousoutput;
445   }
446
447   if (use_pager) {
448     if (fp && !use_mailcap && a->filename) {
449       /* recv case */
450       m_strcpy(pagerfile, sizeof(pagerfile), a->filename);
451       pagerfd = m_tempfd(pagerfile, sizeof(pagerfile), NONULL(Tempdir), pagerfile);
452     } else {
453       pagerfd = m_tempfd(pagerfile, sizeof(pagerfile), NONULL(Tempdir), NULL);
454     }
455   }
456
457   if (use_mailcap) {
458     pid_t thepid = 0;
459     int tempfd = -1;
460
461     if (!use_pager)
462       mutt_endwin (NULL);
463
464     if (use_pager || use_pipe) {
465       if (use_pager && pagerfd == -1) {
466         mutt_perror ("open");
467         goto return_error;
468       }
469       if (use_pipe && ((tempfd = open (tempfile, 0)) == -1)) {
470         if (pagerfd != -1)
471           close (pagerfd);
472         mutt_perror ("open");
473         goto return_error;
474       }
475
476       if ((thepid = mutt_create_filter_fd (command, NULL, NULL, NULL,
477                                            use_pipe ? tempfd : -1,
478                                            use_pager ? pagerfd : -1,
479                                            -1)) == -1) {
480         if (pagerfd != -1)
481           close (pagerfd);
482
483         if (tempfd != -1)
484           close (tempfd);
485
486         mutt_error _("Cannot create filter");
487
488         goto return_error;
489       }
490
491       if (use_pager) {
492         if (a->description)
493           snprintf (descrip, sizeof (descrip),
494                     "---Command: %-20.20s Description: %s",
495                     command, a->description);
496         else
497           snprintf (descrip, sizeof (descrip),
498                     "---Command: %-30.30s Attachment: %s", command, type);
499       }
500
501       if ((mutt_wait_filter (thepid) || (entry->needsterminal &&
502                                          option (OPTWAITKEY))) && !use_pager)
503         mutt_any_key_to_continue (NULL);
504
505       close (tempfd);
506       close (pagerfd);
507
508     }
509     else {
510       /* interactive command */
511       if (mutt_system (command) ||
512           (entry->needsterminal && option (OPTWAITKEY)))
513         mutt_any_key_to_continue (NULL);
514     }
515   }
516   else {
517     /* Don't use mailcap; the attachment is viewed in the pager */
518
519     if (flag == M_AS_TEXT) {
520       /* just let me see the raw data */
521       if (mutt_save_attachment (fp, a, pagerfile, 0, NULL))
522         goto return_error;
523     }
524     else {
525       /* Use built-in handler */
526       set_option (OPTVIEWATTACH);       /* disable the "use 'v' to view this part"
527                                          * message in case of error */
528       if (mutt_decode_save_attachment (fp, a, pagerfile, M_DISPLAY, 0)) {
529         unset_option (OPTVIEWATTACH);
530         goto return_error;
531       }
532       unset_option (OPTVIEWATTACH);
533     }
534
535     if (a->description)
536       m_strcpy(descrip, sizeof(descrip), a->description);
537     else if (a->filename)
538       snprintf (descrip, sizeof (descrip), "---Attachment: %s : %s",
539                 a->filename, type);
540     else
541       snprintf (descrip, sizeof (descrip), "---Attachment: %s", type);
542   }
543
544   /* We only reach this point if there have been no errors */
545
546   if (use_pager) {
547     pager_t info;
548     p_clear(&info, 1);
549
550     info.fp  = fp;
551     info.bdy = a;
552     info.ctx = Context;
553     info.idx = idx;
554     info.idxlen = idxlen;
555     info.hdr = hdr;
556
557     rc = mutt_do_pager(descrip, pagerfile,
558                        M_PAGER_ATTACHMENT | (is_message ? M_PAGER_MESSAGE : 0),
559                        &info);
560     *pagerfile = '\0';
561   }
562   else
563     rc = 0;
564
565 return_error:
566
567   if (entry)
568     rfc1524_entry_delete(&entry);
569   if (fp && tempfile[0])
570     mutt_unlink (tempfile);
571   else if (unlink_tempfile)
572     unlink (tempfile);
573
574   if (pagerfile[0])
575     mutt_unlink (pagerfile);
576
577   return rc;
578 }
579
580 /* returns 1 on success, 0 on error */
581 int mutt_pipe_attachment (FILE * fp, BODY * b, const char *path,
582                           char *outfile)
583 {
584   pid_t thepid;
585   int out = -1;
586   int rv = 0;
587
588   if (outfile && *outfile)
589     if ((out = safe_open (outfile, O_CREAT | O_EXCL | O_WRONLY)) < 0) {
590       mutt_perror ("open");
591       return 0;
592     }
593
594   mutt_endwin (NULL);
595
596   if (fp) {
597     /* recv case */
598     STATE s;
599     p_clear(&s, 1);
600
601     if (outfile && *outfile)
602       thepid = mutt_create_filter_fd(path, &s.fpout, NULL, NULL, -1, out, -1);
603     else
604       thepid = mutt_create_filter(path, &s.fpout, NULL, NULL);
605
606     if (thepid < 0) {
607       mutt_perror (_("Can't create filter"));
608
609       goto bail;
610     }
611
612     s.fpin = fp;
613     mutt_decode_attachment (b, &s);
614     m_fclose(&s.fpout);
615   }
616   else {
617     /* send case */
618
619     FILE *ifp, *ofp;
620
621     if ((ifp = fopen (b->filename, "r")) == NULL) {
622       mutt_perror ("fopen");
623       if (outfile && *outfile) {
624         close (out);
625         unlink (outfile);
626       }
627       return 0;
628     }
629
630     if (outfile && *outfile)
631       thepid = mutt_create_filter_fd (path, &ofp, NULL, NULL, -1, out, -1);
632     else
633       thepid = mutt_create_filter (path, &ofp, NULL, NULL);
634
635     if (thepid < 0) {
636       mutt_perror (_("Can't create filter"));
637
638       m_fclose(&ifp);
639       goto bail;
640     }
641
642     mutt_copy_stream (ifp, ofp);
643     m_fclose(&ofp);
644     m_fclose(&ifp);
645   }
646
647   rv = 1;
648
649 bail:
650
651   if (outfile && *outfile)
652     close (out);
653
654   /*
655    * check for error exit from child process
656    */
657   if (mutt_wait_filter (thepid) != 0)
658     rv = 0;
659
660   if (rv == 0 || option (OPTWAITKEY))
661     mutt_any_key_to_continue (NULL);
662   return rv;
663 }
664
665 static FILE *mutt_save_attachment_open (char *path, int flags)
666 {
667   if (flags == M_SAVE_APPEND)
668     return fopen (path, "a");
669   /* be sure not to change the following fopen to safe_fopen
670    * as safe_fopen returns w/ an error if path exists
671    */
672   if (flags == M_SAVE_OVERWRITE)
673     return fopen (path, "w");   /* __FOPEN_CHECKED__ */
674
675   return safe_fopen (path, "w");
676 }
677
678 /* returns 0 on success, -1 on error */
679 int mutt_save_attachment (FILE * fp, BODY * m, char *path, int flags,
680                           HEADER * hdr)
681 {
682   if (fp) {
683
684     /* recv mode */
685
686     if (hdr &&
687         m->hdr &&
688         m->encoding != ENCBASE64 &&
689         m->encoding != ENCQUOTEDPRINTABLE &&
690         mutt_is_message_type (m->type, m->subtype)) {
691       /* message type attachments are written to mail folders. */
692
693       char buf[HUGE_STRING];
694       HEADER *hn;
695       CONTEXT ctx;
696       MESSAGE *msg;
697       int chflags = 0;
698       int r = -1;
699
700       hn = m->hdr;
701       hn->msgno = hdr->msgno;   /* required for MH/maildir */
702       hn->read = 1;
703
704       fseeko (fp, m->offset, 0);
705       if (fgets (buf, sizeof (buf), fp) == NULL)
706         return -1;
707       if (mx_open_mailbox (path, M_APPEND | M_QUIET, &ctx) == NULL)
708         return -1;
709       if ((msg =
710            mx_open_new_message (&ctx, hn,
711                                 is_from (buf, NULL, 0,
712                                          NULL) ? 0 : M_ADD_FROM)) == NULL) {
713         mx_close_mailbox (&ctx, NULL);
714         return -1;
715       }
716       if (ctx.magic == M_MBOX || ctx.magic == M_MMDF)
717         chflags = CH_FROM;
718       chflags |= (ctx.magic == M_MAILDIR ? CH_NOSTATUS : CH_UPDATE);
719       if (_mutt_copy_message (msg->fp, fp, hn, hn->content, 0, chflags) == 0
720           && mx_commit_message (msg, &ctx) == 0)
721         r = 0;
722       else
723         r = -1;
724
725       mx_close_message (&msg);
726       mx_close_mailbox (&ctx, NULL);
727       return r;
728     }
729     else {
730       /* In recv mode, extract from folder and decode */
731
732       STATE s;
733       p_clear(&s, 1);
734
735       if ((s.fpout = mutt_save_attachment_open (path, flags)) == NULL) {
736         mutt_perror ("fopen");
737         return (-1);
738       }
739       fseeko ((s.fpin = fp), m->offset, 0);
740       mutt_decode_attachment (m, &s);
741
742       if (m_fclose(&s.fpout) != 0) {
743         mutt_perror ("fclose");
744         return (-1);
745       }
746     }
747   } else {
748     /* In send mode, just copy file */
749
750     FILE *ofp, *nfp;
751
752     if ((ofp = fopen (m->filename, "r")) == NULL) {
753       mutt_perror ("fopen");
754       return (-1);
755     }
756
757     if ((nfp = mutt_save_attachment_open (path, flags)) == NULL) {
758       mutt_perror ("fopen");
759       m_fclose(&ofp);
760       return (-1);
761     }
762
763     if (mutt_copy_stream (ofp, nfp) == -1) {
764       mutt_error _("Write fault!");
765
766       m_fclose(&ofp);
767       m_fclose(&nfp);
768       return (-1);
769     }
770     m_fclose(&ofp);
771     m_fclose(&nfp);
772   }
773
774   return 0;
775 }
776
777 /* returns 0 on success, -1 on error */
778 int mutt_decode_save_attachment (FILE * fp, BODY * m, char *path,
779                                  int displaying, int flags)
780 {
781   STATE s;
782   unsigned int saved_encoding = 0;
783   BODY *saved_parts = NULL;
784   HEADER *saved_hdr = NULL;
785
786   p_clear(&s, 1);
787   s.flags = displaying;
788
789   if (flags == M_SAVE_APPEND)
790     s.fpout = fopen (path, "a");
791   else if (flags == M_SAVE_OVERWRITE)
792     s.fpout = safe_fopen (path, "w");   /* __FOPEN_CHECKED__ */
793   else
794     s.fpout = safe_fopen (path, "w");
795
796   if (s.fpout == NULL) {
797     mutt_perror ("fopen");
798     return (-1);
799   }
800
801   if (fp == NULL) {
802     /* When called from the compose menu, the attachment isn't parsed,
803      * so we need to do it here. */
804     struct stat st;
805
806     if (stat (m->filename, &st) == -1) {
807       mutt_perror ("stat");
808       m_fclose(&s.fpout);
809       return (-1);
810     }
811
812     if ((s.fpin = fopen (m->filename, "r")) == NULL) {
813       mutt_perror ("fopen");
814       return (-1);
815     }
816
817     saved_encoding = m->encoding;
818     if (!is_multipart (m))
819       m->encoding = ENC8BIT;
820
821     m->length = st.st_size;
822     m->offset = 0;
823     saved_parts = m->parts;
824     saved_hdr = m->hdr;
825     mutt_parse_part (s.fpin, m);
826
827     if (m->noconv || is_multipart (m))
828       s.flags |= M_CHARCONV;
829   }
830   else {
831     s.fpin = fp;
832     s.flags |= M_CHARCONV;
833   }
834
835   mutt_body_handler (m, &s);
836
837   m_fclose(&s.fpout);
838   if (fp == NULL) {
839     m->length = 0;
840     m->encoding = saved_encoding;
841     if (saved_parts) {
842       header_delete(&m->hdr);
843       m->parts = saved_parts;
844       m->hdr = saved_hdr;
845     }
846     m_fclose(&s.fpin);
847   }
848
849   return (0);
850 }
851
852 /* Ok, the difference between send and receive:
853  * recv: BODY->filename is a suggested name, and Context|HEADER points
854  *       to the attachment in mailbox which is encooded
855  * send: BODY->filename points to the un-encoded file which contains the 
856  *       attachment
857  */
858
859 int mutt_print_attachment (FILE * fp, BODY * a)
860 {
861   char newfile[_POSIX_PATH_MAX] = "";
862   char type[STRING];
863   pid_t thepid;
864   FILE *ifp, *fpout;
865   short unlink_newfile = 0;
866
867   snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
868
869   if (rfc1524_mailcap_lookup (a, type, NULL, M_PRINT)) {
870     char command[_POSIX_PATH_MAX + STRING];
871     rfc1524_entry *entry;
872     int piped = FALSE;
873
874     entry = rfc1524_entry_new();
875     rfc1524_mailcap_lookup (a, type, entry, M_PRINT);
876     if (rfc1524_expand_filename (entry->nametemplate, a->filename,
877                                  newfile, sizeof (newfile))) {
878       if (!fp) {
879         if (safe_symlink (a->filename, newfile) == -1) {
880           if (mutt_yesorno (_("Can't match nametemplate, continue?"), M_YES)
881               != M_YES) {
882             rfc1524_entry_delete(&entry);
883             return 0;
884           }
885           m_strcpy(newfile, sizeof(newfile), a->filename);
886         }
887         else
888           unlink_newfile = 1;
889       }
890     }
891
892     /* in recv mode, save file to newfile first */
893     if (fp)
894       mutt_save_attachment (fp, a, newfile, 0, NULL);
895
896     m_strcpy(command, sizeof(command), entry->printcommand);
897     piped = rfc1524_expand_command(a, newfile, type, command,
898                                    sizeof(command));
899
900     mutt_endwin (NULL);
901
902     /* interactive program */
903     if (piped) {
904       if ((ifp = fopen (newfile, "r")) == NULL) {
905         mutt_perror ("fopen");
906         rfc1524_entry_delete(&entry);
907         return (0);
908       }
909
910       if ((thepid = mutt_create_filter (command, &fpout, NULL, NULL)) < 0) {
911         mutt_perror (_("Can't create filter"));
912
913         rfc1524_entry_delete(&entry);
914         m_fclose(&ifp);
915         return 0;
916       }
917       mutt_copy_stream (ifp, fpout);
918       m_fclose(&fpout);
919       m_fclose(&ifp);
920       if (mutt_wait_filter (thepid) || option (OPTWAITKEY))
921         mutt_any_key_to_continue (NULL);
922     }
923     else {
924       if (mutt_system (command) || option (OPTWAITKEY))
925         mutt_any_key_to_continue (NULL);
926     }
927
928     if (fp)
929       mutt_unlink (newfile);
930     else if (unlink_newfile)
931       unlink (newfile);
932
933     rfc1524_entry_delete(&entry);
934     return (1);
935   }
936
937   if (!ascii_strcasecmp ("text/plain", type) ||
938       !ascii_strcasecmp ("application/postscript", type)) {
939     return (mutt_pipe_attachment (fp, a, NONULL (PrintCmd), NULL));
940   }
941   else if (mutt_can_decode (a)) {
942     /* decode and print */
943
944     int rc = 0;
945
946     ifp = NULL;
947     fpout = NULL;
948
949     mutt_mktemp (newfile);
950     if (mutt_decode_save_attachment (fp, a, newfile, M_PRINTING, 0) == 0) {
951
952       if ((ifp = fopen (newfile, "r")) == NULL) {
953         mutt_perror ("fopen");
954         goto bail0;
955       }
956
957       mutt_endwin (NULL);
958       if ((thepid =
959            mutt_create_filter (NONULL (PrintCmd), &fpout, NULL, NULL)) < 0) {
960         mutt_perror (_("Can't create filter"));
961
962         goto bail0;
963       }
964
965       mutt_copy_stream (ifp, fpout);
966
967       m_fclose(&fpout);
968       m_fclose(&ifp);
969
970       if (mutt_wait_filter (thepid) != 0 || option (OPTWAITKEY))
971         mutt_any_key_to_continue (NULL);
972       rc = 1;
973     }
974   bail0:
975     m_fclose(&ifp);
976     m_fclose(&fpout);
977     mutt_unlink (newfile);
978     return rc;
979   }
980   else {
981     mutt_error _("I don't know how to print that!");
982
983     return 0;
984   }
985 }
986
987 int mutt_attach_check (HEADER* hdr) {
988   int found = 0;
989   char buf[LONG_STRING];
990   char *p = NULL;
991   FILE* fp = NULL;
992   regmatch_t pmatch[1];
993
994   if (!hdr || !hdr->content || !((regex_t*) AttachRemindRegexp.rx) ||
995       (fp = safe_fopen (hdr->content->filename, "r")) == NULL)
996     return (0);
997
998   while (!found && fgets (buf, sizeof (buf), fp)) {
999     p = buf;
1000     while (p && *p) {
1001       if (regexec ((regex_t*) AttachRemindRegexp.rx, p, 1,
1002                   pmatch, 0) == 0) {
1003         found = 1;
1004         break;
1005       }
1006       p++;
1007     }
1008   }
1009   m_fclose(&fp);
1010
1011   return (found);
1012 }