]> git.llucax.com Git - software/mutt-debian.git/blob - recvattach.c
added elinks-lite to B-D, removed links (Closes: 533445)
[software/mutt-debian.git] / recvattach.c
1 /*
2  * Copyright (C) 1996-2000,2002,2007 Michael R. Elkins <me@mutt.org>
3  * Copyright (C) 1999-2006 Thomas Roessler <roessler@does-not-exist.org>
4  * 
5  *     This program is free software; you can redistribute it and/or modify
6  *     it under the terms of the GNU General Public License as published by
7  *     the Free Software Foundation; either version 2 of the License, or
8  *     (at your option) any later version.
9  * 
10  *     This program is distributed in the hope that it will be useful,
11  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *     GNU General Public License for more details.
14  * 
15  *     You should have received a copy of the GNU General Public License
16  *     along with this program; if not, write to the Free Software
17  *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */ 
19
20 #if HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include "mutt.h"
25 #include "mutt_curses.h"
26 #include "mutt_menu.h"
27 #include "rfc1524.h"
28 #include "mime.h"
29 #include "mailbox.h"
30 #include "attach.h"
31 #include "mapping.h"
32 #include "mx.h"
33 #include "mutt_crypt.h"
34
35 #include <ctype.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <sys/wait.h>
39 #include <sys/stat.h>
40 #include <string.h>
41 #include <errno.h>
42
43 static const char *Mailbox_is_read_only = N_("Mailbox is read-only.");
44
45 #define CHECK_READONLY if (Context->readonly) \
46 {\
47     mutt_flushinp (); \
48     mutt_error _(Mailbox_is_read_only); \
49     break; \
50 }
51
52 static struct mapping_t AttachHelp[] = {
53   { N_("Exit"),  OP_EXIT },
54   { N_("Save"),  OP_SAVE },
55   { N_("Pipe"),  OP_PIPE },
56   { N_("Print"), OP_PRINT },
57   { N_("Help"),  OP_HELP },
58   { NULL,        0 }
59 };
60
61 void mutt_update_tree (ATTACHPTR **idx, short idxlen)
62 {
63   char buf[STRING];
64   char *s;
65   int x;
66
67   for (x = 0; x < idxlen; x++)
68   {
69     idx[x]->num = x;
70     if (2 * (idx[x]->level + 2) < sizeof (buf))
71     {
72       if (idx[x]->level)
73       {
74         s = buf + 2 * (idx[x]->level - 1);
75         *s++ = (idx[x]->content->next) ? M_TREE_LTEE : M_TREE_LLCORNER;
76         *s++ = M_TREE_HLINE;
77         *s++ = M_TREE_RARROW;
78       }
79       else
80         s = buf;
81       *s = 0;
82     }
83
84     if (idx[x]->tree)
85     {
86       if (mutt_strcmp (idx[x]->tree, buf) != 0)
87         mutt_str_replace (&idx[x]->tree, buf);
88     }
89     else
90       idx[x]->tree = safe_strdup (buf);
91
92     if (2 * (idx[x]->level + 2) < sizeof (buf) && idx[x]->level)
93     {
94       s = buf + 2 * (idx[x]->level - 1);
95       *s++ = (idx[x]->content->next) ? '\005' : '\006';
96       *s++ = '\006';
97     }
98   }
99 }
100
101 ATTACHPTR **mutt_gen_attach_list (BODY *m,
102                                   int parent_type,
103                                   ATTACHPTR **idx,
104                                   short *idxlen,
105                                   short *idxmax,
106                                   int level,
107                                   int compose)
108 {
109   ATTACHPTR *new;
110   int i;
111   
112   for (; m; m = m->next)
113   {
114     if (*idxlen == *idxmax)
115     {
116       safe_realloc (&idx, sizeof (ATTACHPTR *) * ((*idxmax) += 5));
117       for (i = *idxlen; i < *idxmax; i++)
118         idx[i] = NULL;
119     }
120
121     if (m->type == TYPEMULTIPART && m->parts
122         && (compose || (parent_type == -1 && ascii_strcasecmp ("alternative", m->subtype)))
123         && (!(WithCrypto & APPLICATION_PGP) || !mutt_is_multipart_encrypted(m))
124         )
125     {
126       idx = mutt_gen_attach_list (m->parts, m->type, idx, idxlen, idxmax, level, compose);
127     }
128     else
129     {
130       if (!idx[*idxlen])
131         idx[*idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
132
133       new = idx[(*idxlen)++];
134       new->content = m;
135       m->aptr = new;
136       new->parent_type = parent_type;
137       new->level = level;
138
139       /* We don't support multipart messages in the compose menu yet */
140       if (!compose && !m->collapsed && 
141           ((m->type == TYPEMULTIPART
142             && (!(WithCrypto & APPLICATION_PGP)
143                 || !mutt_is_multipart_encrypted (m))
144             )
145            || mutt_is_message_type(m->type, m->subtype)))
146       {
147         idx = mutt_gen_attach_list (m->parts, m->type, idx, idxlen, idxmax, level + 1, compose);
148       }
149     }
150   }
151
152   if (level == 0)
153     mutt_update_tree (idx, *idxlen);
154
155   return (idx);
156 }
157
158 /* %c = character set: convert?
159  * %C = character set
160  * %D = deleted flag
161  * %d = description
162  * %e = MIME content-transfer-encoding
163  * %f = filename
164  * %I = content-disposition, either I (inline) or A (attachment)
165  * %t = tagged flag
166  * %T = tree chars
167  * %m = major MIME type
168  * %M = MIME subtype
169  * %n = attachment number
170  * %s = size
171  * %u = unlink 
172  */
173 const char *mutt_attach_fmt (char *dest,
174     size_t destlen,
175     size_t col,
176     char op,
177     const char *src,
178     const char *prefix,
179     const char *ifstring,
180     const char *elsestring,
181     unsigned long data,
182     format_flag flags)
183 {
184   char fmt[16];
185   char tmp[SHORT_STRING];
186   char charset[SHORT_STRING];
187   ATTACHPTR *aptr = (ATTACHPTR *) data;
188   int optional = (flags & M_FORMAT_OPTIONAL);
189   size_t l;
190   
191   switch (op)
192   {
193     case 'C':
194       if (!optional)
195       {
196         if (mutt_is_text_part (aptr->content) &&
197             mutt_get_body_charset (charset, sizeof (charset), aptr->content))
198           mutt_format_s (dest, destlen, prefix, charset);
199         else
200           mutt_format_s (dest, destlen, prefix, "");
201       }
202       else if (!mutt_is_text_part (aptr->content) ||
203                !mutt_get_body_charset (charset, sizeof (charset), aptr->content))
204         optional = 0;
205       break;
206     case 'c':
207       /* XXX */
208       if (!optional)
209       {
210         snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
211         snprintf (dest, destlen, fmt, aptr->content->type != TYPETEXT ||
212                   aptr->content->noconv ? 'n' : 'c');
213       }
214       else if (aptr->content->type != TYPETEXT || aptr->content->noconv)
215         optional = 0;
216       break;
217     case 'd':
218       if(!optional)
219       {
220         if (aptr->content->description)
221         {
222           mutt_format_s (dest, destlen, prefix, aptr->content->description);
223           break;
224         }
225         if (mutt_is_message_type(aptr->content->type, aptr->content->subtype) &&
226             MsgFmt && aptr->content->hdr)
227         {
228           char s[SHORT_STRING];
229           _mutt_make_string (s, sizeof (s), MsgFmt, NULL, aptr->content->hdr,
230                              M_FORMAT_FORCESUBJ | M_FORMAT_MAKEPRINT | M_FORMAT_ARROWCURSOR);
231           if (*s)
232           {
233             mutt_format_s (dest, destlen, prefix, s);
234             break;
235           }
236         }
237         if (!aptr->content->filename)
238         {
239           mutt_format_s (dest, destlen, prefix, "<no description>");
240           break;
241         }
242       }
243       else if(aptr->content->description || 
244               (mutt_is_message_type (aptr->content->type, aptr->content->subtype)
245               && MsgFmt && aptr->content->hdr))
246         break;
247     /* FALLS THROUGH TO 'f' */
248     case 'f':
249       if(!optional)
250       {
251         if (aptr->content->filename && *aptr->content->filename == '/')
252         {
253           char path[_POSIX_PATH_MAX];
254           
255           strfcpy (path, aptr->content->filename, sizeof (path));
256           mutt_pretty_mailbox (path, sizeof (path));
257           mutt_format_s (dest, destlen, prefix, path);
258         }
259         else
260           mutt_format_s (dest, destlen, prefix, NONULL (aptr->content->filename));
261       }
262       else if(!aptr->content->filename)
263         optional = 0;
264       break;
265     case 'D':
266       if(!optional)
267         snprintf (dest, destlen, "%c", aptr->content->deleted ? 'D' : ' ');
268       else if(!aptr->content->deleted)
269         optional = 0;
270       break;
271     case 'e':
272       if(!optional)
273         mutt_format_s (dest, destlen, prefix,
274                       ENCODING (aptr->content->encoding));
275       break;
276     case 'I':
277       if (!optional)
278       {
279           snprintf (dest, destlen, "%c",
280                   (aptr->content->disposition == DISPINLINE) ? 'I' : 'A');
281       }
282       break;
283     case 'm':
284       if(!optional)
285         mutt_format_s (dest, destlen, prefix, TYPE (aptr->content));
286       break;
287     case 'M':
288       if(!optional)
289         mutt_format_s (dest, destlen, prefix, aptr->content->subtype);
290       else if(!aptr->content->subtype)
291         optional = 0;
292       break;
293     case 'n':
294       if(!optional)
295       {
296         snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
297         snprintf (dest, destlen, fmt, aptr->num + 1);
298       }
299       break;
300     case 'Q':
301       if (optional)
302         optional = aptr->content->attach_qualifies;
303       else {
304             snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
305         mutt_format_s (dest, destlen, fmt, "Q");
306       }
307       break;
308     case 's':
309       if (flags & M_FORMAT_STAT_FILE)
310       {
311         struct stat st;
312         stat (aptr->content->filename, &st);
313         l = st.st_size;
314       }
315       else
316         l = aptr->content->length;
317       
318       if(!optional)
319       {
320         mutt_pretty_size (tmp, sizeof(tmp), l);
321         mutt_format_s (dest, destlen, prefix, tmp);
322       }
323       else if (l == 0)
324         optional = 0;
325
326       break;
327     case 't':
328       if(!optional)
329         snprintf (dest, destlen, "%c", aptr->content->tagged ? '*' : ' ');
330       else if(!aptr->content->tagged)
331         optional = 0;
332       break;
333     case 'T':
334       if(!optional)
335         mutt_format_s_tree (dest, destlen, prefix, NONULL (aptr->tree));
336       else if (!aptr->tree)
337         optional = 0;
338       break;
339     case 'u':
340       if(!optional)
341         snprintf (dest, destlen, "%c", aptr->content->unlink ? '-' : ' ');
342       else if (!aptr->content->unlink)
343         optional = 0;
344       break;
345     case 'X':
346       if (optional)
347         optional = (aptr->content->attach_count + aptr->content->attach_qualifies) != 0;
348       else
349       {
350         snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
351         snprintf (dest, destlen, fmt, aptr->content->attach_count + aptr->content->attach_qualifies);
352       }
353       break;
354     default:
355       *dest = 0;
356   }
357   
358   if (optional)
359     mutt_FormatString (dest, destlen, col, ifstring, mutt_attach_fmt, data, 0);
360   else if (flags & M_FORMAT_OPTIONAL)
361     mutt_FormatString (dest, destlen, col, elsestring, mutt_attach_fmt, data, 0);
362   return (src);
363 }
364
365 static void attach_entry (char *b, size_t blen, MUTTMENU *menu, int num)
366 {
367   mutt_FormatString (b, blen, 0, NONULL (AttachFormat), mutt_attach_fmt, (unsigned long) (((ATTACHPTR **)menu->data)[num]), M_FORMAT_ARROWCURSOR);
368 }
369
370 int mutt_tag_attach (MUTTMENU *menu, int n, int m)
371 {
372   BODY *cur = ((ATTACHPTR **) menu->data)[n]->content;
373   int ot = cur->tagged;
374   
375   cur->tagged = (m >= 0 ? m : !cur->tagged);
376   return cur->tagged - ot;
377 }
378
379 int mutt_is_message_type (int type, const char *subtype)
380 {
381   if (type != TYPEMESSAGE)
382     return 0;
383
384   subtype = NONULL(subtype);
385   return (ascii_strcasecmp (subtype, "rfc822") == 0 || ascii_strcasecmp (subtype, "news") == 0);
386 }
387
388 static void prepend_curdir (char *dst, size_t dstlen)
389 {
390   size_t l;
391
392   if (!dst || !*dst || *dst == '/' || dstlen < 3 ||
393       /* XXX bad modularization, these are special to mutt_expand_path() */
394       !strchr ("~=+@<>!-^", *dst))
395     return;
396
397   dstlen -= 3;
398   l = strlen (dst) + 2;
399   l = (l > dstlen ? dstlen : l);
400   memmove (dst + 2, dst, l);
401   dst[0] = '.';
402   dst[1] = '/';
403   dst[l + 2] = 0;
404 }
405
406 static int mutt_query_save_attachment (FILE *fp, BODY *body, HEADER *hdr, char **directory)
407 {
408   char *prompt;
409   char buf[_POSIX_PATH_MAX], tfile[_POSIX_PATH_MAX];
410   int is_message;
411   int append = 0;
412   int rc;
413   
414   if (body->filename) 
415   {
416     if (directory && *directory)
417       mutt_concat_path (buf, *directory, mutt_basename (body->filename), sizeof (buf));
418     else
419       strfcpy (buf, body->filename, sizeof (buf));
420   }
421   else if(body->hdr &&
422           body->encoding != ENCBASE64 &&
423           body->encoding != ENCQUOTEDPRINTABLE &&
424           mutt_is_message_type(body->type, body->subtype))
425     mutt_default_save(buf, sizeof(buf), body->hdr);
426   else
427     buf[0] = 0;
428
429   prepend_curdir (buf, sizeof (buf));
430
431   prompt = _("Save to file: ");
432   while (prompt)
433   {
434     if (mutt_get_field (prompt, buf, sizeof (buf), M_FILE | M_CLEAR) != 0
435         || !buf[0])
436     {
437       mutt_clear_error ();
438       return -1;
439     }
440     
441     prompt = NULL;
442     mutt_expand_path (buf, sizeof (buf));
443     
444     is_message = (fp && 
445                   body->hdr && 
446                   body->encoding != ENCBASE64 && 
447                   body->encoding != ENCQUOTEDPRINTABLE && 
448                   mutt_is_message_type (body->type, body->subtype));
449     
450     if (is_message)
451     {
452       struct stat st;
453       
454       /* check to make sure that this file is really the one the user wants */
455       if ((rc = mutt_save_confirm (buf, &st)) == 1)
456       {
457         prompt = _("Save to file: ");
458         continue;
459       } 
460       else if (rc == -1)
461         return -1;
462       strfcpy(tfile, buf, sizeof(tfile));
463     }
464     else
465     {
466       if ((rc = mutt_check_overwrite (body->filename, buf, tfile, sizeof (tfile), &append, directory)) == -1)
467         return -1;
468       else if (rc == 1)
469       {
470         prompt = _("Save to file: ");
471         continue;
472       }
473     }
474     
475     mutt_message _("Saving...");
476     if (mutt_save_attachment (fp, body, tfile, append, (hdr || !is_message) ? hdr : body->hdr) == 0)
477     {
478       mutt_message _("Attachment saved.");
479       return 0;
480     }
481     else
482     {
483       prompt = _("Save to file: ");
484       continue;
485     }
486   }
487   return 0;
488 }
489     
490 void mutt_save_attachment_list (FILE *fp, int tag, BODY *top, HEADER *hdr, MUTTMENU *menu)
491 {
492   char buf[_POSIX_PATH_MAX], tfile[_POSIX_PATH_MAX];
493   char *directory = NULL;
494   int rc = 1;
495   int last = menu ? menu->current : -1;
496   FILE *fpout;
497
498   buf[0] = 0;
499
500   for (; top; top = top->next)
501   {
502     if (!tag || top->tagged)
503     {
504       if (!option (OPTATTACHSPLIT))
505       {
506         if (!buf[0])
507         {
508           int append = 0;
509
510           strfcpy (buf, mutt_basename (NONULL (top->filename)), sizeof (buf));
511           prepend_curdir (buf, sizeof (buf));
512
513           if (mutt_get_field (_("Save to file: "), buf, sizeof (buf),
514                                     M_FILE | M_CLEAR) != 0 || !buf[0])
515             return;
516           mutt_expand_path (buf, sizeof (buf));
517           if (mutt_check_overwrite (top->filename, buf, tfile,
518                                     sizeof (tfile), &append, NULL))
519             return;
520           rc = mutt_save_attachment (fp, top, tfile, append, hdr);
521           if (rc == 0 && AttachSep && (fpout = fopen (tfile,"a")) != NULL)
522           {
523             fprintf(fpout, "%s", AttachSep);
524             safe_fclose (&fpout);
525           }
526         }
527         else
528         {
529           rc = mutt_save_attachment (fp, top, tfile, M_SAVE_APPEND, hdr);
530           if (rc == 0 && AttachSep && (fpout = fopen (tfile,"a")) != NULL)
531           {
532             fprintf(fpout, "%s", AttachSep);
533             safe_fclose (&fpout);
534           }
535         }
536       }
537       else 
538       {
539         if (tag && menu && top->aptr)
540         {
541           menu->oldcurrent = menu->current;
542           menu->current = top->aptr->num;
543           menu_check_recenter (menu);
544           menu->redraw |= REDRAW_MOTION;
545
546           menu_redraw (menu);
547         }
548         if (mutt_query_save_attachment (fp, top, hdr, &directory) == -1)
549           break;
550       }
551     }
552     else if (top->parts)
553       mutt_save_attachment_list (fp, 1, top->parts, hdr, menu);
554     if (!tag)
555       break;
556   }
557
558   FREE (&directory);
559
560   if (tag && menu)
561   {
562     menu->oldcurrent = menu->current;
563     menu->current = last;
564     menu_check_recenter (menu);
565     menu->redraw |= REDRAW_MOTION;
566   }
567   
568   if (!option (OPTATTACHSPLIT) && (rc == 0))
569     mutt_message _("Attachment saved.");
570 }
571
572 static void
573 mutt_query_pipe_attachment (char *command, FILE *fp, BODY *body, int filter)
574 {
575   char tfile[_POSIX_PATH_MAX];
576   char warning[STRING+_POSIX_PATH_MAX];
577
578   if (filter)
579   {
580     snprintf (warning, sizeof (warning),
581               _("WARNING!  You are about to overwrite %s, continue?"),
582               body->filename);
583     if (mutt_yesorno (warning, M_NO) != M_YES) {
584       CLEARLINE (LINES-1);
585       return;
586     }
587     mutt_mktemp (tfile);
588   }
589   else
590     tfile[0] = 0;
591
592   if (mutt_pipe_attachment (fp, body, command, tfile))
593   {
594     if (filter)
595     {
596       mutt_unlink (body->filename);
597       mutt_rename_file (tfile, body->filename);
598       mutt_update_encoding (body);
599       mutt_message _("Attachment filtered.");
600     }
601   }
602   else
603   {
604     if (filter && tfile[0])
605       mutt_unlink (tfile);
606   }
607 }
608
609 static void pipe_attachment (FILE *fp, BODY *b, STATE *state)
610 {
611   FILE *ifp;
612
613   if (fp)
614   {
615     state->fpin = fp;
616     mutt_decode_attachment (b, state);
617     if (AttachSep)
618       state_puts (AttachSep, state);
619   }
620   else
621   {
622     if ((ifp = fopen (b->filename, "r")) == NULL)
623     {
624       mutt_perror ("fopen");
625       return;
626     }
627     mutt_copy_stream (ifp, state->fpout);
628     safe_fclose (&ifp);
629     if (AttachSep)
630       state_puts (AttachSep, state);
631   }
632 }
633
634 static void
635 pipe_attachment_list (char *command, FILE *fp, int tag, BODY *top, int filter,
636                       STATE *state)
637 {
638   for (; top; top = top->next)
639   {
640     if (!tag || top->tagged)
641     {
642       if (!filter && !option (OPTATTACHSPLIT))
643         pipe_attachment (fp, top, state);
644       else
645         mutt_query_pipe_attachment (command, fp, top, filter);
646     }
647     else if (top->parts)
648       pipe_attachment_list (command, fp, tag, top->parts, filter, state);
649     if (!tag)
650       break;
651   }
652 }
653
654 void mutt_pipe_attachment_list (FILE *fp, int tag, BODY *top, int filter)
655 {
656   STATE state;
657   char buf[SHORT_STRING];
658   pid_t thepid;
659
660   if (fp)
661     filter = 0; /* sanity check: we can't filter in the recv case yet */
662
663   buf[0] = 0;
664   memset (&state, 0, sizeof (STATE));
665
666   if (mutt_get_field ((filter ? _("Filter through: ") : _("Pipe to: ")),
667                                   buf, sizeof (buf), M_CMD) != 0 || !buf[0])
668     return;
669
670   mutt_expand_path (buf, sizeof (buf));
671
672   if (!filter && !option (OPTATTACHSPLIT))
673   {
674     mutt_endwin (NULL);
675     thepid = mutt_create_filter (buf, &state.fpout, NULL, NULL);
676     pipe_attachment_list (buf, fp, tag, top, filter, &state);
677     safe_fclose (&state.fpout);
678     if (mutt_wait_filter (thepid) != 0 || option (OPTWAITKEY))
679       mutt_any_key_to_continue (NULL);
680   }
681   else
682     pipe_attachment_list (buf, fp, tag, top, filter, &state);
683 }
684
685 static int can_print (BODY *top, int tag)
686 {
687   char type [STRING];
688
689   for (; top; top = top->next)
690   {
691     snprintf (type, sizeof (type), "%s/%s", TYPE (top), top->subtype);
692     if (!tag || top->tagged)
693     {
694       if (!rfc1524_mailcap_lookup (top, type, NULL, M_PRINT))
695       {
696         if (ascii_strcasecmp ("text/plain", top->subtype) &&
697             ascii_strcasecmp ("application/postscript", top->subtype))
698         {
699           if (!mutt_can_decode (top))
700           {
701             mutt_error (_("I dont know how to print %s attachments!"), type);
702             return (0);
703           }
704         }
705       }
706     }
707     else if (top->parts)
708       return (can_print (top->parts, tag));
709     if (!tag)
710       break;
711   }
712   return (1);
713 }
714
715 static void print_attachment_list (FILE *fp, int tag, BODY *top, STATE *state)
716 {
717   char type [STRING];
718
719
720   for (; top; top = top->next)
721   {
722     if (!tag || top->tagged)
723     {
724       snprintf (type, sizeof (type), "%s/%s", TYPE (top), top->subtype);
725       if (!option (OPTATTACHSPLIT) && !rfc1524_mailcap_lookup (top, type, NULL, M_PRINT))
726       {
727         if (!ascii_strcasecmp ("text/plain", top->subtype) ||
728             !ascii_strcasecmp ("application/postscript", top->subtype))
729           pipe_attachment (fp, top, state);
730         else if (mutt_can_decode (top))
731         {
732           /* decode and print */
733
734           char newfile[_POSIX_PATH_MAX] = "";
735           FILE *ifp;
736
737           mutt_mktemp (newfile);
738           if (mutt_decode_save_attachment (fp, top, newfile, M_PRINTING, 0) == 0)
739           {
740             if ((ifp = fopen (newfile, "r")) != NULL)
741             {
742               mutt_copy_stream (ifp, state->fpout);
743               safe_fclose (&ifp);
744               if (AttachSep)
745                 state_puts (AttachSep, state);
746             }
747           }
748           mutt_unlink (newfile);
749         }
750       }
751       else
752         mutt_print_attachment (fp, top);
753     }
754     else if (top->parts)
755       print_attachment_list (fp, tag, top->parts, state);
756     if (!tag)
757       return;
758   }
759 }
760
761 void mutt_print_attachment_list (FILE *fp, int tag, BODY *top)
762 {
763   STATE state;
764   
765   pid_t thepid;
766   if (query_quadoption (OPT_PRINT, tag ? _("Print tagged attachment(s)?") : _("Print attachment?")) != M_YES)
767     return;
768
769   if (!option (OPTATTACHSPLIT))
770   {
771     if (!can_print (top, tag))
772       return;
773     mutt_endwin (NULL);
774     memset (&state, 0, sizeof (STATE));
775     thepid = mutt_create_filter (NONULL (PrintCmd), &state.fpout, NULL, NULL);
776     print_attachment_list (fp, tag, top, &state);
777     safe_fclose (&state.fpout);
778     if (mutt_wait_filter (thepid) != 0 || option (OPTWAITKEY))
779       mutt_any_key_to_continue (NULL);
780   }
781   else
782     print_attachment_list (fp, tag, top, &state);
783 }
784
785 static void
786 mutt_update_attach_index (BODY *cur, ATTACHPTR ***idxp,
787                                       short *idxlen, short *idxmax,
788                                       MUTTMENU *menu)
789 {
790   ATTACHPTR **idx = *idxp;
791   while (--(*idxlen) >= 0)
792     idx[(*idxlen)]->content = NULL;
793   *idxlen = 0;
794
795   idx = *idxp = mutt_gen_attach_list (cur, -1, idx, idxlen, idxmax, 0, 0);
796   
797   menu->max  = *idxlen;
798   menu->data = *idxp;
799
800   if (menu->current >= menu->max)
801     menu->current = menu->max - 1;
802   menu_check_recenter (menu);
803   menu->redraw |= REDRAW_INDEX;
804   
805 }
806
807
808 int
809 mutt_attach_display_loop (MUTTMENU *menu, int op, FILE *fp, HEADER *hdr,
810                           BODY *cur, ATTACHPTR ***idxp, short *idxlen, short *idxmax,
811                           int recv)
812 {
813   ATTACHPTR **idx = *idxp;
814 #if 0
815   int old_optweed = option (OPTWEED);
816   set_option (OPTWEED);
817 #endif
818   
819   do
820   {
821     switch (op)
822     {
823       case OP_DISPLAY_HEADERS:
824         toggle_option (OPTWEED);
825         /* fall through */
826
827       case OP_VIEW_ATTACH:
828         op = mutt_view_attachment (fp, idx[menu->current]->content, M_REGULAR,
829                                    hdr, idx, *idxlen);
830         break;
831
832       case OP_NEXT_ENTRY:
833       case OP_MAIN_NEXT_UNDELETED: /* hack */
834         if (menu->current < menu->max - 1)
835         {
836           menu->current++;
837           op = OP_VIEW_ATTACH;
838         }
839         else
840           op = OP_NULL;
841         break;
842       case OP_PREV_ENTRY:
843       case OP_MAIN_PREV_UNDELETED: /* hack */
844         if (menu->current > 0)
845         {
846           menu->current--;
847           op = OP_VIEW_ATTACH;
848         }
849         else
850           op = OP_NULL;
851         break;
852       case OP_EDIT_TYPE:
853         /* when we edit the content-type, we should redisplay the attachment
854            immediately */
855         mutt_edit_content_type (hdr, idx[menu->current]->content, fp);
856         if (idxmax)
857         {
858           mutt_update_attach_index (cur, idxp, idxlen, idxmax, menu);
859           idx = *idxp;
860         }
861         op = OP_VIEW_ATTACH;
862         break;
863       /* functions which are passed through from the pager */
864       case OP_CHECK_TRADITIONAL:
865         if (!(WithCrypto & APPLICATION_PGP) || (hdr && hdr->security & PGP_TRADITIONAL_CHECKED))
866         {
867           op = OP_NULL;
868           break;
869         }
870         /* fall through */
871       case OP_ATTACH_COLLAPSE:
872         if (recv)
873           return op;
874       default:
875         op = OP_NULL;
876     }
877   }
878   while (op != OP_NULL);
879
880 #if 0
881   if (option (OPTWEED) != old_optweed)
882     toggle_option (OPTWEED);
883 #endif
884   return op;
885 }
886
887 static void attach_collapse (BODY *b, short collapse, short init, short just_one)
888 {
889   short i;
890   for (; b; b = b->next)
891   {
892     i = init || b->collapsed;
893     if (i && option (OPTDIGESTCOLLAPSE) && b->type == TYPEMULTIPART
894         && !ascii_strcasecmp (b->subtype, "digest"))
895       attach_collapse (b->parts, 1, 1, 0);
896     else if (b->type == TYPEMULTIPART || mutt_is_message_type (b->type, b->subtype))
897       attach_collapse (b->parts, collapse, i, 0);
898     b->collapsed = collapse;
899     if (just_one)
900       return;
901   }
902 }
903
904 void mutt_attach_init (BODY *b)
905 {
906   for (; b; b = b->next)
907   {
908     b->tagged = 0;
909     b->collapsed = 0;
910     if (b->parts) 
911       mutt_attach_init (b->parts);
912   }
913 }
914
915 static const char *Function_not_permitted = N_("Function not permitted in attach-message mode.");
916
917 #define CHECK_ATTACH if(option(OPTATTACHMSG)) \
918                      {\
919                         mutt_flushinp (); \
920                         mutt_error _(Function_not_permitted); \
921                         break; \
922                      }
923
924
925
926
927 void mutt_view_attachments (HEADER *hdr)
928 {
929   int secured = 0;
930   int need_secured = 0;
931
932   char helpstr[LONG_STRING];
933   MUTTMENU *menu;
934   BODY *cur = NULL;
935   MESSAGE *msg;
936   FILE *fp;
937   ATTACHPTR **idx = NULL;
938   short idxlen = 0;
939   short idxmax = 0;
940   int flags = 0;
941   int op = OP_NULL;
942   
943   /* make sure we have parsed this message */
944   mutt_parse_mime_message (Context, hdr);
945
946   mutt_message_hook (Context, hdr, M_MESSAGEHOOK);
947   
948   if ((msg = mx_open_message (Context, hdr->msgno)) == NULL)
949     return;
950
951
952   if (WithCrypto && ((hdr->security & ENCRYPT) ||
953                      (mutt_is_application_smime(hdr->content) & SMIMEOPAQUE)))
954   {
955     need_secured  = 1;
956
957     if ((hdr->security & ENCRYPT) && !crypt_valid_passphrase(hdr->security))
958     {
959       mx_close_message (&msg);
960       return;
961     }
962     if ((WithCrypto & APPLICATION_SMIME) && (hdr->security & APPLICATION_SMIME))
963     {
964       if (hdr->env)
965           crypt_smime_getkeys (hdr->env);
966
967       if (mutt_is_application_smime(hdr->content))
968       {
969         secured = ! crypt_smime_decrypt_mime (msg->fp, &fp,
970                                               hdr->content, &cur);
971         
972         /* S/MIME nesting */
973         if ((mutt_is_application_smime (cur) & SMIMEOPAQUE))
974         {
975           BODY *_cur = cur;
976           FILE *_fp = fp;
977           
978           fp = NULL; cur = NULL;
979           secured = !crypt_smime_decrypt_mime (_fp, &fp, _cur, &cur);
980           
981           mutt_free_body (&_cur);
982           safe_fclose (&_fp);
983         }
984       }
985       else
986         need_secured = 0;
987     }
988     if ((WithCrypto & APPLICATION_PGP) && (hdr->security & APPLICATION_PGP))
989     {
990       if (mutt_is_multipart_encrypted(hdr->content))
991         secured = !crypt_pgp_decrypt_mime (msg->fp, &fp, hdr->content, &cur);
992       else
993         need_secured = 0;
994     }
995
996     if (need_secured && !secured)
997     {
998       mx_close_message (&msg);
999       mutt_error _("Can't decrypt encrypted message!");
1000       return;
1001     }
1002   }
1003   
1004   if (!WithCrypto || !need_secured)
1005   {
1006     fp = msg->fp;
1007     cur = hdr->content;
1008   }
1009
1010   menu = mutt_new_menu (MENU_ATTACH);
1011   menu->title = _("Attachments");
1012   menu->make_entry = attach_entry;
1013   menu->tag = mutt_tag_attach;
1014   menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_ATTACH, AttachHelp);
1015
1016   mutt_attach_init (cur);
1017   attach_collapse (cur, 0, 1, 0);
1018   mutt_update_attach_index (cur, &idx, &idxlen, &idxmax, menu);
1019
1020   FOREVER
1021   {
1022     if (op == OP_NULL)
1023       op = mutt_menuLoop (menu);
1024     switch (op)
1025     {
1026       case OP_ATTACH_VIEW_MAILCAP:
1027         mutt_view_attachment (fp, idx[menu->current]->content, M_MAILCAP,
1028                               hdr, idx, idxlen);
1029         menu->redraw = REDRAW_FULL;
1030         break;
1031
1032       case OP_ATTACH_VIEW_TEXT:
1033         mutt_view_attachment (fp, idx[menu->current]->content, M_AS_TEXT,
1034                               hdr, idx, idxlen);
1035         menu->redraw = REDRAW_FULL;
1036         break;
1037
1038       case OP_DISPLAY_HEADERS:
1039       case OP_VIEW_ATTACH:
1040         op = mutt_attach_display_loop (menu, op, fp, hdr, cur, &idx, &idxlen, &idxmax, 1);
1041         menu->redraw = REDRAW_FULL;
1042         continue;
1043
1044       case OP_ATTACH_COLLAPSE:
1045         if (!idx[menu->current]->content->parts)
1046         {
1047           mutt_error _("There are no subparts to show!");
1048           break;
1049         }
1050         if (!idx[menu->current]->content->collapsed)
1051           attach_collapse (idx[menu->current]->content, 1, 0, 1);
1052         else
1053           attach_collapse (idx[menu->current]->content, 0, 1, 1);
1054         mutt_update_attach_index (cur, &idx, &idxlen, &idxmax, menu);
1055         break;
1056       
1057       case OP_FORGET_PASSPHRASE:
1058         crypt_forget_passphrase ();
1059         break;
1060
1061       case OP_EXTRACT_KEYS:
1062         if ((WithCrypto & APPLICATION_PGP))
1063         {
1064           crypt_pgp_extract_keys_from_attachment_list (fp, menu->tagprefix, 
1065                     menu->tagprefix ? cur : idx[menu->current]->content);
1066           menu->redraw = REDRAW_FULL;
1067         }
1068         break;
1069       
1070       case OP_CHECK_TRADITIONAL:
1071         if ((WithCrypto & APPLICATION_PGP)
1072             && crypt_pgp_check_traditional (fp, menu->tagprefix ? cur
1073                                               : idx[menu->current]->content,
1074                                       menu->tagprefix))
1075         {
1076           hdr->security = crypt_query (cur);
1077           menu->redraw = REDRAW_FULL;
1078         }
1079         break;
1080
1081       case OP_PRINT:
1082         mutt_print_attachment_list (fp, menu->tagprefix, 
1083                   menu->tagprefix ? cur : idx[menu->current]->content);
1084         break;
1085
1086       case OP_PIPE:
1087         mutt_pipe_attachment_list (fp, menu->tagprefix, 
1088                   menu->tagprefix ? cur : idx[menu->current]->content, 0);
1089         break;
1090
1091       case OP_SAVE:
1092         mutt_save_attachment_list (fp, menu->tagprefix, 
1093                   menu->tagprefix ?  cur : idx[menu->current]->content, hdr, menu);
1094
1095         if (!menu->tagprefix && option (OPTRESOLVE) && menu->current < menu->max - 1)
1096           menu->current++;
1097       
1098         menu->redraw = REDRAW_MOTION_RESYNCH | REDRAW_FULL;
1099         break;
1100
1101       case OP_DELETE:
1102         CHECK_READONLY;
1103
1104 #ifdef USE_POP
1105         if (Context->magic == M_POP)
1106         {
1107           mutt_flushinp ();
1108           mutt_error _("Can't delete attachment from POP server.");
1109           break;
1110         }
1111 #endif
1112
1113         if (WithCrypto && hdr->security & ~PGP_TRADITIONAL_CHECKED)
1114         {
1115           mutt_message _(
1116             "Deletion of attachments from encrypted messages is unsupported.");
1117         }
1118         else
1119         {
1120           if (!menu->tagprefix)
1121           {
1122             if (idx[menu->current]->parent_type == TYPEMULTIPART)
1123             {
1124               idx[menu->current]->content->deleted = 1;
1125               if (option (OPTRESOLVE) && menu->current < menu->max - 1)
1126               {
1127                 menu->current++;
1128                 menu->redraw = REDRAW_MOTION_RESYNCH;
1129               }
1130               else
1131                 menu->redraw = REDRAW_CURRENT;
1132             }
1133             else
1134               mutt_message _(
1135                 "Only deletion of multipart attachments is supported.");
1136           }
1137           else
1138           {
1139             int x;
1140
1141             for (x = 0; x < menu->max; x++)
1142             {
1143               if (idx[x]->content->tagged)
1144               {
1145                 if (idx[x]->parent_type == TYPEMULTIPART)
1146                 {
1147                   idx[x]->content->deleted = 1;
1148                   menu->redraw = REDRAW_INDEX;
1149                 }
1150                 else
1151                   mutt_message _(
1152                     "Only deletion of multipart attachments is supported.");
1153               }
1154             }
1155           }
1156         }
1157         break;
1158
1159       case OP_UNDELETE:
1160        CHECK_READONLY;
1161        if (!menu->tagprefix)
1162        {
1163          idx[menu->current]->content->deleted = 0;
1164          if (option (OPTRESOLVE) && menu->current < menu->max - 1)
1165          {
1166            menu->current++;
1167            menu->redraw = REDRAW_MOTION_RESYNCH;
1168          }
1169          else
1170            menu->redraw = REDRAW_CURRENT;
1171        }
1172        else
1173        {
1174          int x;
1175
1176          for (x = 0; x < menu->max; x++)
1177          {
1178            if (idx[x]->content->tagged)
1179            {
1180              idx[x]->content->deleted = 0;
1181              menu->redraw = REDRAW_INDEX;
1182            }
1183          }
1184        }
1185        break;
1186
1187       case OP_RESEND:
1188         CHECK_ATTACH;
1189         mutt_attach_resend (fp, hdr, idx, idxlen,
1190                              menu->tagprefix ? NULL : idx[menu->current]->content);
1191         menu->redraw = REDRAW_FULL;
1192         break;
1193       
1194       case OP_BOUNCE_MESSAGE:
1195         CHECK_ATTACH;
1196         mutt_attach_bounce (fp, hdr, idx, idxlen,
1197                              menu->tagprefix ? NULL : idx[menu->current]->content);
1198         menu->redraw = REDRAW_FULL;
1199         break;
1200
1201       case OP_FORWARD_MESSAGE:
1202         CHECK_ATTACH;
1203         mutt_attach_forward (fp, hdr, idx, idxlen,
1204                              menu->tagprefix ? NULL : idx[menu->current]->content);
1205         menu->redraw = REDRAW_FULL;
1206         break;
1207       
1208       case OP_REPLY:
1209       case OP_GROUP_REPLY:
1210       case OP_LIST_REPLY:
1211
1212         CHECK_ATTACH;
1213       
1214         flags = SENDREPLY | 
1215           (op == OP_GROUP_REPLY ? SENDGROUPREPLY : 0) |
1216           (op == OP_LIST_REPLY ? SENDLISTREPLY : 0);
1217         mutt_attach_reply (fp, hdr, idx, idxlen, 
1218                            menu->tagprefix ? NULL : idx[menu->current]->content, flags);
1219         menu->redraw = REDRAW_FULL;
1220         break;
1221
1222       case OP_EDIT_TYPE:
1223         mutt_edit_content_type (hdr, idx[menu->current]->content, fp);
1224         mutt_update_attach_index (cur, &idx, &idxlen, &idxmax, menu);
1225         break;
1226
1227       case OP_EXIT:
1228         mx_close_message (&msg);
1229         hdr->attach_del = 0;
1230         while (idxmax-- > 0)
1231         {
1232           if (!idx[idxmax])
1233             continue;
1234           if (idx[idxmax]->content && idx[idxmax]->content->deleted)
1235             hdr->attach_del = 1;
1236           if (idx[idxmax]->content)
1237             idx[idxmax]->content->aptr = NULL;
1238           FREE (&idx[idxmax]->tree);
1239           FREE (&idx[idxmax]);
1240         }
1241         if (hdr->attach_del)
1242           hdr->changed = 1;
1243         FREE (&idx);
1244         idxmax = 0;
1245
1246         if (WithCrypto && need_secured && secured)
1247         {
1248           safe_fclose (&fp);
1249           mutt_free_body (&cur);
1250         }
1251
1252         mutt_menuDestroy  (&menu);
1253         return;
1254     }
1255
1256     op = OP_NULL;
1257   }
1258
1259   /* not reached */
1260 }