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