]> granicus.if.org Git - mutt/blob - compose.c
Convert pgp_app_handler to use buffer pool.
[mutt] / compose.c
1 /*
2  * Copyright (C) 1996-2000,2002,2007,2010,2012 Michael R. Elkins <me@mutt.org>
3  * Copyright (C) 2004 g10 Code GmbH
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 "version.h"
25 #include "mutt.h"
26 #include "mutt_curses.h"
27 #include "mutt_idna.h"
28 #include "mutt_menu.h"
29 #include "mutt_crypt.h"
30 #include "rfc1524.h"
31 #include "mime.h"
32 #include "attach.h"
33 #include "mapping.h"
34 #include "mailbox.h"
35 #include "sort.h"
36 #include "charset.h"
37 #include "rfc3676.h"
38
39 #ifdef MIXMASTER
40 #include "remailer.h"
41 #endif
42
43 #ifdef USE_AUTOCRYPT
44 #include "autocrypt/autocrypt.h"
45 #endif
46
47 #include <errno.h>
48 #include <string.h>
49 #include <sys/stat.h>
50 #include <sys/wait.h>
51 #include <unistd.h>
52 #include <stdlib.h>
53
54 static const char* There_are_no_attachments = N_("There are no attachments.");
55
56 #define CHECK_COUNT if (actx->idxlen == 0) { mutt_error _(There_are_no_attachments); break; }
57
58 #define CURATTACH actx->idx[actx->v2r[menu->current]]
59
60
61 enum
62 {
63   HDR_FROM  = 0,
64   HDR_TO,
65   HDR_CC,
66   HDR_BCC,
67   HDR_SUBJECT,
68   HDR_REPLYTO,
69   HDR_FCC,
70
71 #ifdef MIXMASTER
72   HDR_MIX,
73 #endif
74
75   HDR_CRYPT,
76   HDR_CRYPTINFO,
77 #ifdef USE_AUTOCRYPT
78   HDR_AUTOCRYPT,
79 #endif
80
81   HDR_ATTACH_TITLE,     /* the "-- Attachments" line */
82   HDR_ATTACH            /* where to start printing the attachments */
83 };
84
85 int HeaderPadding[HDR_ATTACH_TITLE] = {0};
86 int MaxHeaderWidth = 0;
87
88 #define HDR_XOFFSET MaxHeaderWidth
89 #define W (MuttIndexWindow->cols - MaxHeaderWidth)
90
91 static const char * const Prompts[] =
92 {
93   /* L10N: Compose menu field.  May not want to translate. */
94   N_("From: "),
95   /* L10N: Compose menu field.  May not want to translate. */
96   N_("To: "),
97   /* L10N: Compose menu field.  May not want to translate. */
98   N_("Cc: "),
99   /* L10N: Compose menu field.  May not want to translate. */
100   N_("Bcc: "),
101   /* L10N: Compose menu field.  May not want to translate. */
102   N_("Subject: "),
103   /* L10N: Compose menu field.  May not want to translate. */
104   N_("Reply-To: "),
105   /* L10N: Compose menu field.  May not want to translate. */
106   N_("Fcc: "),
107 #ifdef MIXMASTER
108   /* L10N: "Mix" refers to the MixMaster chain for anonymous email */
109   N_("Mix: "),
110 #endif
111   /* L10N: Compose menu field.  Holds "Encrypt", "Sign" related information */
112   N_("Security: "),
113   /* L10N:
114    * This string is used by the compose menu.
115    * Since it is hidden by default, it does not increase the
116    * indentation of other compose menu fields.  However, if possible,
117    * it should not be longer than the other compose menu fields.
118    *
119    * Since it shares the row with "Encrypt with:", it should not be longer
120    * than 15-20 character cells.
121    */
122   N_("Sign as: "),
123 #ifdef USE_AUTOCRYPT
124   /* L10N:
125      The compose menu autocrypt line
126    */
127   N_("Autocrypt: ")
128 #endif
129 };
130
131 static const struct mapping_t ComposeHelp[] = {
132   { N_("Send"),    OP_COMPOSE_SEND_MESSAGE },
133   { N_("Abort"),   OP_EXIT },
134   /* L10N: compose menu help line entry */
135   { N_("To"),      OP_COMPOSE_EDIT_TO },
136   /* L10N: compose menu help line entry */
137   { N_("CC"),      OP_COMPOSE_EDIT_CC },
138   /* L10N: compose menu help line entry */
139   { N_("Subj"),    OP_COMPOSE_EDIT_SUBJECT },
140   { N_("Attach file"),  OP_COMPOSE_ATTACH_FILE },
141   { N_("Descrip"), OP_COMPOSE_EDIT_DESCRIPTION },
142   { N_("Help"),    OP_HELP },
143   { NULL,       0 }
144 };
145
146 #ifdef USE_AUTOCRYPT
147 static const char *AutocryptRecUiFlags[] = {
148   /* L10N: Autocrypt recommendation flag: off.
149    * This is displayed when Autocrypt is turned off. */
150   N_("Off"),
151   /* L10N: Autocrypt recommendation flag: no.
152    * This is displayed when Autocrypt cannot encrypt to the recipients. */
153   N_("No"),
154   /* L10N: Autocrypt recommendation flag: discouraged.
155    * This is displayed when Autocrypt believes encryption should not be used.
156    * This might occur if one of the recipient Autocrypt Keys has not been
157    * used recently, or if the only key available is a Gossip Header key. */
158   N_("Discouraged"),
159   /* L10N: Autocrypt recommendation flag: available.
160    * This is displayed when Autocrypt believes encryption is possible, but
161    * leaves enabling it up to the sender.  Probably because "prefer encrypt"
162    * is not set in both the sender and recipient keys. */
163   N_("Available"),
164   /* L10N: Autocrypt recommendation flag: yes.
165    * This is displayed when Autocrypt would normally enable encryption
166    * automatically. */
167   N_("Yes"),
168 };
169 #endif
170
171 typedef struct
172 {
173   HEADER *msg;
174   char *fcc;
175 #ifdef USE_AUTOCRYPT
176   autocrypt_rec_t autocrypt_rec;
177   int autocrypt_rec_override;
178 #endif
179 } compose_redraw_data_t;
180
181 static void calc_header_width_padding (int idx, const char *header, int calc_max)
182 {
183   int width;
184
185   HeaderPadding[idx] = mutt_strlen (header);
186   width = mutt_strwidth (header);
187   if (calc_max && MaxHeaderWidth < width)
188     MaxHeaderWidth = width;
189   HeaderPadding[idx] -= width;
190 }
191
192
193 /* The padding needed for each header is strlen() + max_width - strwidth().
194  *
195  * calc_header_width_padding sets each entry in HeaderPadding to
196  * strlen - width.  Then, afterwards, we go through and add max_width
197  * to each entry.
198  */
199 static void init_header_padding (void)
200 {
201   static short done = 0;
202   int i;
203
204   if (done)
205     return;
206   done = 1;
207
208   for (i = 0; i < HDR_ATTACH_TITLE; i++)
209   {
210     if (i == HDR_CRYPTINFO)
211       continue;
212     calc_header_width_padding (i, _(Prompts[i]), 1);
213   }
214
215   /* Don't include "Sign as: " in the MaxHeaderWidth calculation.  It
216    * doesn't show up by default, and so can make the indentation of
217    * the other fields look funny. */
218   calc_header_width_padding (HDR_CRYPTINFO, _(Prompts[HDR_CRYPTINFO]), 0);
219
220   for (i = 0; i < HDR_ATTACH_TITLE; i++)
221   {
222     HeaderPadding[i] += MaxHeaderWidth;
223     if (HeaderPadding[i] < 0)
224       HeaderPadding[i] = 0;
225   }
226 }
227
228 static void snd_entry (char *b, size_t blen, MUTTMENU *menu, int num)
229 {
230   ATTACH_CONTEXT *actx = (ATTACH_CONTEXT *)menu->data;
231
232   mutt_FormatString (b, blen, 0, MuttIndexWindow->cols, NONULL (AttachFormat), mutt_attach_fmt,
233                      (unsigned long)(actx->idx[actx->v2r[num]]),
234                      MUTT_FORMAT_STAT_FILE | MUTT_FORMAT_ARROWCURSOR);
235 }
236
237 #ifdef USE_AUTOCRYPT
238 static void autocrypt_compose_menu (HEADER *msg)
239 {
240   char *prompt, *letters;
241   int choice;
242
243   /* L10N:
244      The compose menu autocrypt prompt.
245      (e)ncrypt enables encryption via autocrypt.
246      (c)lear sets cleartext.
247      (a)utomatic defers to the recommendation.
248   */
249   prompt = _("Autocrypt: (e)ncrypt, (c)lear, (a)utomatic? ");
250
251   /* L10N:
252      The letter corresponding to the compose menu autocrypt prompt
253      (e)ncrypt, (c)lear, (a)utomatic
254    */
255   letters = _("eca");
256
257   choice = mutt_multi_choice (prompt, letters);
258   switch (choice)
259   {
260     case 1:
261       msg->security |= (AUTOCRYPT | AUTOCRYPT_OVERRIDE);
262       msg->security &= ~(ENCRYPT | SIGN | OPPENCRYPT | INLINE);
263       break;
264     case 2:
265       msg->security &= ~AUTOCRYPT;
266       msg->security |= AUTOCRYPT_OVERRIDE;
267       break;
268     case 3:
269       msg->security &= ~AUTOCRYPT_OVERRIDE;
270       if (option (OPTCRYPTOPPORTUNISTICENCRYPT))
271         msg->security |= OPPENCRYPT;
272       break;
273   }
274 }
275 #endif
276
277 static void redraw_crypt_lines (compose_redraw_data_t *rd)
278 {
279   HEADER *msg = rd->msg;
280
281   SETCOLOR (MT_COLOR_COMPOSE_HEADER);
282   mutt_window_mvprintw (MuttIndexWindow, HDR_CRYPT, 0,
283                         "%*s", HeaderPadding[HDR_CRYPT], _(Prompts[HDR_CRYPT]));
284   NORMAL_COLOR;
285
286   if ((WithCrypto & (APPLICATION_PGP | APPLICATION_SMIME)) == 0)
287   {
288     addstr(_("Not supported"));
289     return;
290   }
291
292   if ((msg->security & (ENCRYPT | SIGN)) == (ENCRYPT | SIGN))
293   {
294     SETCOLOR (MT_COLOR_COMPOSE_SECURITY_BOTH);
295     addstr (_("Sign, Encrypt"));
296   }
297   else if (msg->security & ENCRYPT)
298   {
299     SETCOLOR (MT_COLOR_COMPOSE_SECURITY_ENCRYPT);
300     addstr (_("Encrypt"));
301   }
302   else if (msg->security & SIGN)
303   {
304     SETCOLOR (MT_COLOR_COMPOSE_SECURITY_SIGN);
305     addstr (_("Sign"));
306   }
307   else
308   {
309     SETCOLOR (MT_COLOR_COMPOSE_SECURITY_NONE);
310     addstr (_("None"));
311   }
312   NORMAL_COLOR;
313
314   if ((msg->security & (ENCRYPT | SIGN)))
315   {
316     if ((WithCrypto & APPLICATION_PGP) && (msg->security & APPLICATION_PGP))
317     {
318       if ((msg->security & INLINE))
319         addstr (_(" (inline PGP)"));
320       else
321         addstr (_(" (PGP/MIME)"));
322     }
323     else if ((WithCrypto & APPLICATION_SMIME) &&
324              (msg->security & APPLICATION_SMIME))
325       addstr (_(" (S/MIME)"));
326   }
327
328   if (option (OPTCRYPTOPPORTUNISTICENCRYPT) && (msg->security & OPPENCRYPT))
329     addstr (_(" (OppEnc mode)"));
330
331   mutt_window_clrtoeol (MuttIndexWindow);
332   mutt_window_move (MuttIndexWindow, HDR_CRYPTINFO, 0);
333   mutt_window_clrtoeol (MuttIndexWindow);
334
335   if ((WithCrypto & APPLICATION_PGP)
336       && (msg->security & APPLICATION_PGP) && (msg->security & SIGN))
337   {
338     SETCOLOR (MT_COLOR_COMPOSE_HEADER);
339     printw ("%*s", HeaderPadding[HDR_CRYPTINFO], _(Prompts[HDR_CRYPTINFO]));
340     NORMAL_COLOR;
341     printw ("%s", PgpSignAs ? PgpSignAs : _("<default>"));
342   }
343
344   if ((WithCrypto & APPLICATION_SMIME)
345       && (msg->security & APPLICATION_SMIME) && (msg->security & SIGN))
346   {
347     SETCOLOR (MT_COLOR_COMPOSE_HEADER);
348     printw ("%*s", HeaderPadding[HDR_CRYPTINFO], _(Prompts[HDR_CRYPTINFO]));
349     NORMAL_COLOR;
350     printw ("%s", SmimeSignAs ? SmimeSignAs : _("<default>"));
351   }
352
353   if ((WithCrypto & APPLICATION_SMIME)
354       && (msg->security & APPLICATION_SMIME)
355       && (msg->security & ENCRYPT)
356       && SmimeCryptAlg)
357   {
358     SETCOLOR (MT_COLOR_COMPOSE_HEADER);
359     mutt_window_mvprintw (MuttIndexWindow, HDR_CRYPTINFO, 40, "%s", _("Encrypt with: "));
360     NORMAL_COLOR;
361     printw ("%s", NONULL(SmimeCryptAlg));
362   }
363
364 #ifdef USE_AUTOCRYPT
365   mutt_window_move (MuttIndexWindow, HDR_AUTOCRYPT, 0);
366   mutt_window_clrtoeol (MuttIndexWindow);
367   if (option (OPTAUTOCRYPT))
368   {
369     SETCOLOR (MT_COLOR_COMPOSE_HEADER);
370     printw ("%*s", HeaderPadding[HDR_AUTOCRYPT], _(Prompts[HDR_AUTOCRYPT]));
371     NORMAL_COLOR;
372     if (msg->security & AUTOCRYPT)
373     {
374       SETCOLOR (MT_COLOR_COMPOSE_SECURITY_ENCRYPT);
375       addstr (_("Encrypt"));
376     }
377     else
378     {
379       SETCOLOR (MT_COLOR_COMPOSE_SECURITY_NONE);
380       addstr (_("Off"));
381     }
382
383     SETCOLOR (MT_COLOR_COMPOSE_HEADER);
384     mutt_window_mvprintw (MuttIndexWindow, HDR_AUTOCRYPT, 40, "%s",
385                           /* L10N:
386                              The autocrypt compose menu Recommendation field.
387                              Displays the output of the recommendation engine
388                              (Off, No, Discouraged, Available, Yes)
389                           */
390                           _("Recommendation: "));
391     NORMAL_COLOR;
392     printw ("%s", _(AutocryptRecUiFlags[rd->autocrypt_rec]));
393   }
394 #endif
395 }
396
397 static void update_crypt_info (compose_redraw_data_t *rd)
398 {
399   HEADER *msg = rd->msg;
400
401   if (option (OPTCRYPTOPPORTUNISTICENCRYPT))
402     crypt_opportunistic_encrypt (msg);
403
404 #ifdef USE_AUTOCRYPT
405   if (option (OPTAUTOCRYPT))
406   {
407     rd->autocrypt_rec = mutt_autocrypt_ui_recommendation (msg, NULL);
408
409     /* Anything that enables ENCRYPT or SIGN, or turns on SMIME
410      * overrides autocrypt, be it oppenc or the user having turned on
411      * those flags manually. */
412     if (msg->security & (ENCRYPT | SIGN | APPLICATION_SMIME))
413       msg->security &= ~(AUTOCRYPT | AUTOCRYPT_OVERRIDE);
414     else
415     {
416       if (!(msg->security & AUTOCRYPT_OVERRIDE))
417       {
418         if (rd->autocrypt_rec == AUTOCRYPT_REC_YES)
419         {
420           msg->security |= AUTOCRYPT;
421           msg->security &= ~INLINE;
422         }
423         else
424           msg->security &= ~AUTOCRYPT;
425       }
426     }
427   }
428 #endif
429
430   redraw_crypt_lines (rd);
431 }
432
433
434 #ifdef MIXMASTER
435
436 static void redraw_mix_line (LIST *chain)
437 {
438   int c;
439   char *t;
440
441   SETCOLOR (MT_COLOR_COMPOSE_HEADER);
442   mutt_window_mvprintw (MuttIndexWindow, HDR_MIX, 0,
443                         "%*s", HeaderPadding[HDR_MIX], _(Prompts[HDR_MIX]));
444   NORMAL_COLOR;
445
446   if (!chain)
447   {
448     addstr ("<no chain defined>");
449     mutt_window_clrtoeol (MuttIndexWindow);
450     return;
451   }
452
453   for (c = 12; chain; chain = chain->next)
454   {
455     t = chain->data;
456     if (t && t[0] == '0' && t[1] == '\0')
457       t = "<random>";
458
459     if (c + mutt_strlen (t) + 2 >= MuttIndexWindow->cols)
460       break;
461
462     addstr (NONULL(t));
463     if (chain->next)
464       addstr (", ");
465
466     c += mutt_strlen (t) + 2;
467   }
468 }
469 #endif /* MIXMASTER */
470
471 static int
472 check_attachments(ATTACH_CONTEXT *actx)
473 {
474   int i, r, rc = -1;
475   struct stat st;
476   BUFFER *pretty = NULL, *msg = NULL;
477
478   for (i = 0; i < actx->idxlen; i++)
479   {
480     if (stat(actx->idx[i]->content->filename, &st) != 0)
481     {
482       if (!pretty)
483         pretty = mutt_buffer_pool_get ();
484       mutt_buffer_strcpy (pretty, actx->idx[i]->content->filename);
485       mutt_buffer_pretty_mailbox (pretty);
486       /* L10N:
487          This message is displayed in the compose menu when an attachment
488          doesn't stat.  %d is the attachment number and %s is the
489          attachment filename.
490          The filename is located last to avoid a long path hiding the
491          error message.
492       */
493       mutt_error (_("Attachment #%d no longer exists: %s"),
494                   i+1, mutt_b2s (pretty));
495       goto cleanup;
496     }
497
498     if (actx->idx[i]->content->stamp < st.st_mtime)
499     {
500       if (!pretty)
501         pretty = mutt_buffer_pool_get ();
502       mutt_buffer_strcpy (pretty, actx->idx[i]->content->filename);
503       mutt_buffer_pretty_mailbox (pretty);
504
505       if (!msg)
506         msg = mutt_buffer_pool_get ();
507       /* L10N:
508          This message is displayed in the compose menu when an attachment
509          is modified behind the scenes.  %d is the attachment number
510          and %s is the attachment filename.
511          The filename is located last to avoid a long path hiding the
512          prompt question.
513       */
514       mutt_buffer_printf (msg, _("Attachment #%d modified. Update encoding for %s?"),
515                           i+1, mutt_b2s (pretty));
516
517       if ((r = mutt_yesorno (mutt_b2s (msg), MUTT_YES)) == MUTT_YES)
518         mutt_update_encoding (actx->idx[i]->content);
519       else if (r == -1)
520         goto cleanup;
521     }
522   }
523
524   rc = 0;
525
526 cleanup:
527   mutt_buffer_pool_release (&pretty);
528   mutt_buffer_pool_release (&msg);
529   return rc;
530 }
531
532 static void draw_envelope_addr (int line, ADDRESS *addr)
533 {
534   char buf[LONG_STRING];
535
536   buf[0] = 0;
537   rfc822_write_address (buf, sizeof (buf), addr, 1);
538   SETCOLOR (MT_COLOR_COMPOSE_HEADER);
539   mutt_window_mvprintw (MuttIndexWindow, line, 0,
540                         "%*s", HeaderPadding[line], _(Prompts[line]));
541   NORMAL_COLOR;
542   mutt_paddstr (W, buf);
543 }
544
545 static void draw_envelope (compose_redraw_data_t *rd)
546 {
547   HEADER *msg = rd->msg;
548   char *fcc = rd->fcc;
549
550   draw_envelope_addr (HDR_FROM, msg->env->from);
551   draw_envelope_addr (HDR_TO, msg->env->to);
552   draw_envelope_addr (HDR_CC, msg->env->cc);
553   draw_envelope_addr (HDR_BCC, msg->env->bcc);
554
555   SETCOLOR (MT_COLOR_COMPOSE_HEADER);
556   mutt_window_mvprintw (MuttIndexWindow, HDR_SUBJECT, 0,
557                         "%*s", HeaderPadding[HDR_SUBJECT], _(Prompts[HDR_SUBJECT]));
558   NORMAL_COLOR;
559   mutt_paddstr (W, NONULL (msg->env->subject));
560
561   draw_envelope_addr (HDR_REPLYTO, msg->env->reply_to);
562
563   SETCOLOR (MT_COLOR_COMPOSE_HEADER);
564   mutt_window_mvprintw (MuttIndexWindow, HDR_FCC, 0,
565                         "%*s", HeaderPadding[HDR_FCC], _(Prompts[HDR_FCC]));
566   NORMAL_COLOR;
567   mutt_paddstr (W, fcc);
568
569   if (WithCrypto)
570     redraw_crypt_lines (rd);
571
572 #ifdef MIXMASTER
573   redraw_mix_line (msg->chain);
574 #endif
575
576   SETCOLOR (MT_COLOR_STATUS);
577   mutt_window_mvaddstr (MuttIndexWindow, HDR_ATTACH_TITLE, 0, _("-- Attachments"));
578   mutt_window_clrtoeol (MuttIndexWindow);
579
580   NORMAL_COLOR;
581 }
582
583 static void edit_address_list (int line, ADDRESS **addr)
584 {
585   char buf[HUGE_STRING] = ""; /* needs to be large for alias expansion */
586   char *err = NULL;
587
588   mutt_addrlist_to_local (*addr);
589   rfc822_write_address (buf, sizeof (buf), *addr, 0);
590   if (mutt_get_field (_(Prompts[line]), buf, sizeof (buf), MUTT_ALIAS) == 0)
591   {
592     rfc822_free_address (addr);
593     *addr = mutt_parse_adrlist (*addr, buf);
594     *addr = mutt_expand_aliases (*addr);
595   }
596
597   if (mutt_addrlist_to_intl (*addr, &err) != 0)
598   {
599     mutt_error (_("Warning: '%s' is a bad IDN."), err);
600     mutt_refresh();
601     FREE (&err);
602   }
603
604   /* redraw the expanded list so the user can see the result */
605   buf[0] = 0;
606   rfc822_write_address (buf, sizeof (buf), *addr, 1);
607   mutt_window_move (MuttIndexWindow, line, HDR_XOFFSET);
608   mutt_paddstr (W, buf);
609 }
610
611 static int delete_attachment (ATTACH_CONTEXT *actx, int x)
612 {
613   ATTACHPTR **idx = actx->idx;
614   int rindex = actx->v2r[x];
615   int y;
616
617   if (rindex == 0 && actx->idxlen == 1)
618   {
619     mutt_error _("You may not delete the only attachment.");
620     idx[rindex]->content->tagged = 0;
621     return (-1);
622   }
623
624   for (y = 0; y < actx->idxlen; y++)
625   {
626     if (idx[y]->content->next == idx[rindex]->content)
627     {
628       idx[y]->content->next = idx[rindex]->content->next;
629       break;
630     }
631   }
632
633   idx[rindex]->content->next = NULL;
634   /* mutt_make_message_attach() creates body->parts, shared by
635    * body->hdr->content.  If we NULL out that, it creates a memory
636    * leak because mutt_free_body() frees body->parts, not
637    * body->hdr->content.
638    *
639    * Other ci_send_message() message constructors are careful to free
640    * any body->parts, removing depth:
641    *  - mutt_prepare_template() used by postponed, resent, and draft files
642    *  - mutt_copy_body() used by the recvattach menu and $forward_attachments.
643    *
644    * I believe it is safe to completely remove the "content->parts =
645    * NULL" statement.  But for safety, am doing so only for the case
646    * it must be avoided: message attachments.
647    */
648   if (!idx[rindex]->content->hdr)
649     idx[rindex]->content->parts = NULL;
650   mutt_free_body (&(idx[rindex]->content));
651   FREE (&idx[rindex]->tree);
652   FREE (&idx[rindex]);
653   for (; rindex < actx->idxlen - 1; rindex++)
654     idx[rindex] = idx[rindex+1];
655   idx[actx->idxlen - 1] = NULL;
656   actx->idxlen--;
657
658   return (0);
659 }
660
661 static void mutt_gen_compose_attach_list (ATTACH_CONTEXT *actx,
662                                           BODY *m,
663                                           int parent_type,
664                                           int level)
665 {
666   ATTACHPTR *new;
667
668   for (; m; m = m->next)
669   {
670     if (m->type == TYPEMULTIPART && m->parts
671         && (!(WithCrypto & APPLICATION_PGP) || !mutt_is_multipart_encrypted(m))
672       )
673     {
674       mutt_gen_compose_attach_list (actx, m->parts, m->type, level);
675     }
676     else
677     {
678       new = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
679       mutt_actx_add_attach (actx, new);
680       new->content = m;
681       m->aptr = new;
682       new->parent_type = parent_type;
683       new->level = level;
684
685       /* We don't support multipart messages in the compose menu yet */
686     }
687   }
688 }
689
690 static void mutt_update_compose_menu (ATTACH_CONTEXT *actx, MUTTMENU *menu, int init)
691 {
692   if (init)
693   {
694     mutt_gen_compose_attach_list (actx, actx->hdr->content, -1, 0);
695     mutt_attach_init (actx);
696     menu->data = actx;
697   }
698
699   mutt_update_tree (actx);
700
701   menu->max = actx->vcount;
702   if (menu->max)
703   {
704     if (menu->current >= menu->max)
705       menu->current = menu->max - 1;
706   }
707   else
708     menu->current = 0;
709
710   menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
711 }
712
713 static void update_idx (MUTTMENU *menu, ATTACH_CONTEXT *actx, ATTACHPTR *new)
714 {
715   new->level = (actx->idxlen > 0) ? actx->idx[actx->idxlen-1]->level : 0;
716   if (actx->idxlen)
717     actx->idx[actx->idxlen - 1]->content->next = new->content;
718   new->content->aptr = new;
719   mutt_actx_add_attach (actx, new);
720   mutt_update_compose_menu (actx, menu, 0);
721   menu->current = actx->vcount - 1;
722 }
723
724
725 /*
726  * cum_attachs_size: Cumulative Attachments Size
727  *
728  * Returns the total number of bytes used by the attachments in the
729  * attachment list _after_ content-transfer-encodings have been
730  * applied.
731  *
732  */
733
734 static unsigned long cum_attachs_size (MUTTMENU *menu)
735 {
736   size_t s;
737   unsigned short i;
738   ATTACH_CONTEXT *actx = menu->data;
739   ATTACHPTR **idx = actx->idx;
740   CONTENT *info;
741   BODY *b;
742
743   for (i = 0, s = 0; i < actx->idxlen; i++)
744   {
745     b = idx[i]->content;
746
747     if (!b->content)
748       b->content = mutt_get_content_info (b->filename, b);
749
750     if ((info = b->content))
751     {
752       switch (b->encoding)
753       {
754         case ENCQUOTEDPRINTABLE:
755           s += 3 * (info->lobin + info->hibin) + info->ascii + info->crlf;
756           break;
757         case ENCBASE64:
758           s += (4 * (info->lobin + info->hibin + info->ascii + info->crlf)) / 3;
759           break;
760         default:
761           s += info->lobin + info->hibin + info->ascii + info->crlf;
762           break;
763       }
764     }
765   }
766
767   return s;
768 }
769
770 /* prototype for use below */
771 static void compose_status_line (char *buf, size_t buflen, size_t col, int cols, MUTTMENU *menu,
772                                  const char *p);
773
774 /*
775  * compose_format_str()
776  *
777  * %a = total number of attachments
778  * %h = hostname  [option]
779  * %l = approx. length of current message (in bytes)
780  * %v = Mutt version
781  *
782  * This function is similar to status_format_str().  Look at that function for
783  * help when modifying this function.
784  */
785
786 static const char *
787 compose_format_str (char *buf, size_t buflen, size_t col, int cols, char op, const char *src,
788                     const char *prefix, const char *ifstring,
789                     const char *elsestring,
790                     unsigned long data, format_flag flags)
791 {
792   char fmt[SHORT_STRING], tmp[SHORT_STRING];
793   int optional = (flags & MUTT_FORMAT_OPTIONAL);
794   MUTTMENU *menu = (MUTTMENU *) data;
795
796   *buf = 0;
797   switch (op)
798   {
799     case 'a': /* total number of attachments */
800       snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
801       snprintf (buf, buflen, fmt, menu->max);
802       break;
803
804     case 'h':  /* hostname */
805       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
806       snprintf (buf, buflen, fmt, NONULL(Hostname));
807       break;
808
809     case 'l': /* approx length of current message in bytes */
810       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
811       mutt_pretty_size (tmp, sizeof (tmp), menu ? cum_attachs_size(menu) : 0);
812       snprintf (buf, buflen, fmt, tmp);
813       break;
814
815     case 'v':
816       snprintf (fmt, sizeof (fmt), "Mutt %%s");
817       snprintf (buf, buflen, fmt, MUTT_VERSION);
818       break;
819
820     case 0:
821       *buf = 0;
822       return (src);
823
824     default:
825       snprintf (buf, buflen, "%%%s%c", prefix, op);
826       break;
827   }
828
829   if (optional)
830     compose_status_line (buf, buflen, col, cols, menu, ifstring);
831   else if (flags & MUTT_FORMAT_OPTIONAL)
832     compose_status_line (buf, buflen, col, cols, menu, elsestring);
833
834   return (src);
835 }
836
837 static void compose_status_line (char *buf, size_t buflen, size_t col, int cols,
838                                  MUTTMENU *menu, const char *p)
839 {
840   mutt_FormatString (buf, buflen, col, cols, p, compose_format_str,
841                      (unsigned long) menu, 0);
842 }
843
844 static void compose_menu_redraw (MUTTMENU *menu)
845 {
846   char buf[LONG_STRING];
847   compose_redraw_data_t *rd = menu->redraw_data;
848
849   if (!rd)
850     return;
851
852   if (menu->redraw & REDRAW_FULL)
853   {
854     menu_redraw_full (menu);
855
856     draw_envelope (rd);
857     menu->offset = HDR_ATTACH;
858     menu->pagelen = MuttIndexWindow->rows - HDR_ATTACH;
859   }
860
861   menu_check_recenter (menu);
862
863   if (menu->redraw & REDRAW_STATUS)
864   {
865     compose_status_line (buf, sizeof (buf), 0, MuttStatusWindow->cols, menu, NONULL(ComposeFormat));
866     mutt_window_move (MuttStatusWindow, 0, 0);
867     SETCOLOR (MT_COLOR_STATUS);
868     mutt_paddstr (MuttStatusWindow->cols, buf);
869     NORMAL_COLOR;
870     menu->redraw &= ~REDRAW_STATUS;
871   }
872
873 #ifdef USE_SIDEBAR
874   if (menu->redraw & REDRAW_SIDEBAR)
875     menu_redraw_sidebar (menu);
876 #endif
877
878   if (menu->redraw & REDRAW_INDEX)
879     menu_redraw_index (menu);
880   else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
881     menu_redraw_motion (menu);
882   else if (menu->redraw == REDRAW_CURRENT)
883     menu_redraw_current (menu);
884 }
885
886
887 /* return values:
888  *
889  * 1    message should be postponed
890  * 0    normal exit
891  * -1   abort message
892  */
893 int mutt_compose_menu (HEADER *msg,   /* structure for new message */
894                        char *fcc,     /* where to save a copy of the message */
895                        size_t fcclen,
896                        HEADER *cur,   /* current message */
897                        int flags)
898 {
899   char helpstr[LONG_STRING];
900   char buf[LONG_STRING];
901   BUFFER *fname = NULL;
902   MUTTMENU *menu;
903   ATTACH_CONTEXT *actx;
904   ATTACHPTR *new;
905   int i, close = 0;
906   int r = -1;           /* return value */
907   int op = 0;
908   int loop = 1;
909   int fccSet = 0;       /* has the user edited the Fcc: field ? */
910   CONTEXT *ctx = NULL, *this = NULL;
911   /* Sort, SortAux could be changed in mutt_index_menu() */
912   int oldSort, oldSortAux;
913   struct stat st;
914   compose_redraw_data_t rd = {0};
915
916   init_header_padding ();
917
918   rd.msg = msg;
919   rd.fcc = fcc;
920
921   menu = mutt_new_menu (MENU_COMPOSE);
922   menu->offset = HDR_ATTACH;
923   menu->make_entry = snd_entry;
924   menu->tag = mutt_tag_attach;
925   menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE, ComposeHelp);
926   menu->custom_menu_redraw = compose_menu_redraw;
927   menu->redraw_data = &rd;
928   mutt_push_current_menu (menu);
929
930   actx = safe_calloc (sizeof(ATTACH_CONTEXT), 1);
931   actx->hdr = msg;
932   mutt_update_compose_menu (actx, menu, 1);
933
934   update_crypt_info (&rd);
935
936   /* Since this is rather long lived, we don't use the pool */
937   fname = mutt_buffer_new ();
938   mutt_buffer_increase_size (fname, LONG_STRING);
939
940   while (loop)
941   {
942     switch (op = mutt_menuLoop (menu))
943     {
944       case OP_COMPOSE_EDIT_FROM:
945         edit_address_list (HDR_FROM, &msg->env->from);
946         update_crypt_info (&rd);
947         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
948         break;
949       case OP_COMPOSE_EDIT_TO:
950         edit_address_list (HDR_TO, &msg->env->to);
951         update_crypt_info (&rd);
952         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
953         break;
954       case OP_COMPOSE_EDIT_BCC:
955         edit_address_list (HDR_BCC, &msg->env->bcc);
956         update_crypt_info (&rd);
957         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
958         break;
959       case OP_COMPOSE_EDIT_CC:
960         edit_address_list (HDR_CC, &msg->env->cc);
961         update_crypt_info (&rd);
962         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
963         break;
964       case OP_COMPOSE_EDIT_SUBJECT:
965         if (msg->env->subject)
966           strfcpy (buf, msg->env->subject, sizeof (buf));
967         else
968           buf[0] = 0;
969         if (mutt_get_field (_("Subject: "), buf, sizeof (buf), 0) == 0)
970         {
971           mutt_str_replace (&msg->env->subject, buf);
972           mutt_window_move (MuttIndexWindow, HDR_SUBJECT, HDR_XOFFSET);
973           if (msg->env->subject)
974             mutt_paddstr (W, msg->env->subject);
975           else
976             mutt_window_clrtoeol(MuttIndexWindow);
977         }
978         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
979         break;
980       case OP_COMPOSE_EDIT_REPLY_TO:
981         edit_address_list (HDR_REPLYTO, &msg->env->reply_to);
982         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
983         break;
984       case OP_COMPOSE_EDIT_FCC:
985         strfcpy (buf, fcc, sizeof (buf));
986         if (mutt_get_field (_("Fcc: "), buf, sizeof (buf), MUTT_FILE | MUTT_CLEAR) == 0)
987         {
988           strfcpy (fcc, buf, fcclen);
989           mutt_pretty_mailbox (fcc, fcclen);
990           mutt_window_move (MuttIndexWindow, HDR_FCC, HDR_XOFFSET);
991           mutt_paddstr (W, fcc);
992           fccSet = 1;
993         }
994         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
995         break;
996       case OP_COMPOSE_EDIT_MESSAGE:
997         if (Editor && (mutt_strcmp ("builtin", Editor) != 0) && !option (OPTEDITHDRS))
998         {
999           mutt_rfc3676_space_unstuff (msg);
1000           mutt_edit_file (Editor, msg->content->filename);
1001           mutt_rfc3676_space_stuff (msg);
1002           mutt_update_encoding (msg->content);
1003           menu->redraw = REDRAW_FULL;
1004           mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1005           break;
1006         }
1007         /* fall through */
1008       case OP_COMPOSE_EDIT_HEADERS:
1009         mutt_rfc3676_space_unstuff (msg);
1010
1011         if (mutt_strcmp ("builtin", Editor) != 0 &&
1012             (op == OP_COMPOSE_EDIT_HEADERS ||
1013              (op == OP_COMPOSE_EDIT_MESSAGE && option (OPTEDITHDRS))))
1014         {
1015           char *tag = NULL, *err = NULL;
1016           mutt_env_to_local (msg->env);
1017           mutt_edit_headers (NONULL (Editor), msg->content->filename, msg,
1018                              fcc, fcclen);
1019           if (mutt_env_to_intl (msg->env, &tag, &err))
1020           {
1021             mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err);
1022             FREE (&err);
1023           }
1024           update_crypt_info (&rd);
1025         }
1026         else
1027         {
1028           /* this is grouped with OP_COMPOSE_EDIT_HEADERS because the
1029              attachment list could change if the user invokes ~v to edit
1030              the message with headers, in which we need to execute the
1031              code below to regenerate the index array */
1032           mutt_builtin_editor (msg->content->filename, msg, cur);
1033         }
1034
1035         mutt_rfc3676_space_stuff (msg);
1036         mutt_update_encoding (msg->content);
1037
1038         /* attachments may have been added */
1039         if (actx->idxlen && actx->idx[actx->idxlen - 1]->content->next)
1040         {
1041           mutt_actx_free_entries (actx);
1042           mutt_update_compose_menu (actx, menu, 1);
1043         }
1044
1045         menu->redraw = REDRAW_FULL;
1046         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1047         break;
1048
1049
1050
1051       case OP_COMPOSE_ATTACH_KEY:
1052         if (!(WithCrypto & APPLICATION_PGP))
1053           break;
1054
1055         new = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
1056         if ((new->content = crypt_pgp_make_key_attachment(NULL)) != NULL)
1057         {
1058           update_idx (menu, actx, new);
1059           menu->redraw |= REDRAW_INDEX;
1060         }
1061         else
1062           FREE (&new);
1063
1064         menu->redraw |= REDRAW_STATUS;
1065
1066         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1067         break;
1068
1069
1070       case OP_COMPOSE_ATTACH_FILE:
1071       {
1072         char *prompt, **files;
1073         int error, numfiles;
1074
1075         mutt_buffer_clear (fname);
1076         prompt = _("Attach file");
1077         numfiles = 0;
1078         files = NULL;
1079
1080         if ((_mutt_buffer_enter_fname (prompt, fname, 0, 1, &files, &numfiles) == -1) ||
1081             !mutt_buffer_len (fname))
1082           break;
1083
1084         error = 0;
1085         if (numfiles > 1)
1086           mutt_message _("Attaching selected files...");
1087         for (i = 0; i < numfiles; i++)
1088         {
1089           char *att = files[i];
1090           new = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
1091           new->unowned = 1;
1092           new->content = mutt_make_file_attach (att);
1093           if (new->content != NULL)
1094             update_idx (menu, actx, new);
1095           else
1096           {
1097             error = 1;
1098             mutt_error (_("Unable to attach %s!"), att);
1099             FREE (&new);
1100           }
1101           FREE (&files[i]);
1102         }
1103
1104         FREE (&files);
1105         if (!error) mutt_clear_error ();
1106
1107         menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
1108       }
1109       mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1110       break;
1111
1112       case OP_COMPOSE_ATTACH_MESSAGE:
1113       {
1114         char *prompt;
1115         HEADER *h;
1116
1117         mutt_buffer_clear (fname);
1118         prompt = _("Open mailbox to attach message from");
1119
1120         if (Context)
1121         {
1122           mutt_buffer_strcpy (fname, NONULL (Context->path));
1123           mutt_buffer_pretty_mailbox (fname);
1124         }
1125
1126         if ((mutt_buffer_enter_fname (prompt, fname, 1) == -1) ||
1127             !mutt_buffer_len (fname))
1128           break;
1129
1130         mutt_buffer_expand_path (fname);
1131 #ifdef USE_IMAP
1132         if (!mx_is_imap (mutt_b2s (fname)))
1133 #endif
1134 #ifdef USE_POP
1135           if (!mx_is_pop (mutt_b2s (fname)))
1136 #endif
1137             /* check to make sure the file exists and is readable */
1138             if (access (mutt_b2s (fname), R_OK) == -1)
1139             {
1140               mutt_perror (mutt_b2s (fname));
1141               break;
1142             }
1143
1144         menu->redraw = REDRAW_FULL;
1145
1146         ctx = mx_open_mailbox (mutt_b2s (fname), MUTT_READONLY, NULL);
1147         if (ctx == NULL)
1148         {
1149           mutt_error (_("Unable to open mailbox %s"), mutt_b2s (fname));
1150           break;
1151         }
1152
1153         if (!ctx->msgcount)
1154         {
1155           mx_close_mailbox (ctx, NULL);
1156           FREE (&ctx);
1157           mutt_error _("No messages in that folder.");
1158           break;
1159         }
1160
1161         this = Context; /* remember current folder and sort methods*/
1162         oldSort = Sort; oldSortAux = SortAux;
1163
1164         Context = ctx;
1165         set_option(OPTATTACHMSG);
1166         mutt_message _("Tag the messages you want to attach!");
1167         close = mutt_index_menu ();
1168         unset_option(OPTATTACHMSG);
1169
1170         if (!Context)
1171         {
1172           /* go back to the folder we started from */
1173           Context = this;
1174           /* Restore old $sort and $sort_aux */
1175           Sort = oldSort;
1176           SortAux = oldSortAux;
1177           menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
1178           break;
1179         }
1180
1181         for (i = 0; i < Context->msgcount; i++)
1182         {
1183           h = Context->hdrs[i];
1184           if (h->tagged)
1185           {
1186             new = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
1187             new->content = mutt_make_message_attach (Context, h, 1);
1188             if (new->content != NULL)
1189               update_idx (menu, actx, new);
1190             else
1191             {
1192               mutt_error _("Unable to attach!");
1193               FREE (&new);
1194             }
1195           }
1196         }
1197         menu->redraw |= REDRAW_FULL;
1198
1199         if (close == OP_QUIT)
1200           mx_close_mailbox (Context, NULL);
1201         else
1202           mx_fastclose_mailbox (Context);
1203         FREE (&Context);
1204
1205         /* go back to the folder we started from */
1206         Context = this;
1207         /* Restore old $sort and $sort_aux */
1208         Sort = oldSort;
1209         SortAux = oldSortAux;
1210       }
1211       mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1212       break;
1213
1214       case OP_DELETE:
1215         CHECK_COUNT;
1216         if (CURATTACH->unowned)
1217           CURATTACH->content->unlink = 0;
1218         if (delete_attachment (actx, menu->current) == -1)
1219           break;
1220         mutt_update_compose_menu (actx, menu, 0);
1221         if (menu->current == 0)
1222           msg->content = actx->idx[0]->content;
1223
1224         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1225         break;
1226
1227       case OP_COMPOSE_TOGGLE_RECODE:
1228       {
1229         CHECK_COUNT;
1230         if (!mutt_is_text_part (CURATTACH->content))
1231         {
1232           mutt_error (_("Recoding only affects text attachments."));
1233           break;
1234         }
1235         CURATTACH->content->noconv = !CURATTACH->content->noconv;
1236         if (CURATTACH->content->noconv)
1237           mutt_message (_("The current attachment won't be converted."));
1238         else
1239           mutt_message (_("The current attachment will be converted."));
1240         menu->redraw = REDRAW_CURRENT;
1241         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1242         break;
1243       }
1244
1245       case OP_COMPOSE_EDIT_DESCRIPTION:
1246         CHECK_COUNT;
1247         strfcpy (buf,
1248                  CURATTACH->content->description ?
1249                  CURATTACH->content->description : "",
1250                  sizeof (buf));
1251         /* header names should not be translated */
1252         if (mutt_get_field ("Description: ", buf, sizeof (buf), 0) == 0)
1253         {
1254           mutt_str_replace (&CURATTACH->content->description, buf);
1255           menu->redraw = REDRAW_CURRENT;
1256         }
1257         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1258         break;
1259
1260       case OP_COMPOSE_UPDATE_ENCODING:
1261         CHECK_COUNT;
1262         if (menu->tagprefix)
1263         {
1264           BODY *top;
1265           for (top = msg->content; top; top = top->next)
1266           {
1267             if (top->tagged)
1268               mutt_update_encoding (top);
1269           }
1270           menu->redraw = REDRAW_FULL;
1271         }
1272         else
1273         {
1274           mutt_update_encoding(CURATTACH->content);
1275           menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
1276         }
1277         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1278         break;
1279
1280       case OP_COMPOSE_TOGGLE_DISPOSITION:
1281         /* toggle the content-disposition between inline/attachment */
1282         CURATTACH->content->disposition = (CURATTACH->content->disposition == DISPINLINE) ? DISPATTACH : DISPINLINE;
1283         menu->redraw = REDRAW_CURRENT;
1284         break;
1285
1286       case OP_EDIT_TYPE:
1287         CHECK_COUNT;
1288         {
1289           mutt_edit_content_type (NULL, CURATTACH->content, NULL);
1290
1291           /* this may have been a change to text/something */
1292           mutt_update_encoding (CURATTACH->content);
1293
1294           menu->redraw = REDRAW_CURRENT;
1295         }
1296         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1297         break;
1298
1299       case OP_COMPOSE_EDIT_ENCODING:
1300         CHECK_COUNT;
1301         strfcpy (buf, ENCODING (CURATTACH->content->encoding),
1302                  sizeof (buf));
1303         if (mutt_get_field ("Content-Transfer-Encoding: ", buf,
1304                             sizeof (buf), 0) == 0 && buf[0])
1305         {
1306           if ((i = mutt_check_encoding (buf)) != ENCOTHER && i != ENCUUENCODED)
1307           {
1308             CURATTACH->content->encoding = i;
1309             menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
1310             mutt_clear_error();
1311           }
1312           else
1313             mutt_error _("Invalid encoding.");
1314         }
1315         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1316         break;
1317
1318       case OP_COMPOSE_SEND_MESSAGE:
1319
1320         /* Note: We don't invoke send2-hook here, since we want to leave
1321          * users an opportunity to change settings from the ":" prompt.
1322          */
1323
1324         if (check_attachments(actx) != 0)
1325         {
1326           menu->redraw = REDRAW_FULL;
1327           break;
1328         }
1329
1330
1331 #ifdef MIXMASTER
1332         if (msg->chain && mix_check_message (msg) != 0)
1333           break;
1334 #endif
1335
1336         if (!fccSet && *fcc)
1337         {
1338           if ((i = query_quadoption (OPT_COPY,
1339                                      _("Save a copy of this message?"))) == -1)
1340             break;
1341           else if (i == MUTT_NO)
1342             *fcc = 0;
1343         }
1344
1345         loop = 0;
1346         r = 0;
1347         break;
1348
1349       case OP_COMPOSE_EDIT_FILE:
1350         CHECK_COUNT;
1351         mutt_edit_file (NONULL(Editor), CURATTACH->content->filename);
1352         mutt_update_encoding (CURATTACH->content);
1353         menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
1354         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1355         break;
1356
1357       case OP_COMPOSE_TOGGLE_UNLINK:
1358         CHECK_COUNT;
1359         CURATTACH->content->unlink = !CURATTACH->content->unlink;
1360         menu->redraw = REDRAW_INDEX;
1361         /* No send2hook since this doesn't change the message. */
1362         break;
1363
1364       case OP_COMPOSE_GET_ATTACHMENT:
1365         CHECK_COUNT;
1366         if (menu->tagprefix)
1367         {
1368           BODY *top;
1369           for (top = msg->content; top; top = top->next)
1370           {
1371             if (top->tagged)
1372               mutt_get_tmp_attachment(top);
1373           }
1374           menu->redraw = REDRAW_FULL;
1375         }
1376         else if (mutt_get_tmp_attachment(CURATTACH->content) == 0)
1377           menu->redraw = REDRAW_CURRENT;
1378
1379         /* No send2hook since this doesn't change the message. */
1380         break;
1381
1382       case OP_COMPOSE_RENAME_ATTACHMENT:
1383       {
1384         char *src;
1385         int ret;
1386
1387         CHECK_COUNT;
1388         if (CURATTACH->content->d_filename)
1389           src = CURATTACH->content->d_filename;
1390         else
1391           src = CURATTACH->content->filename;
1392         mutt_buffer_strcpy (fname, mutt_basename (NONULL (src)));
1393         ret = mutt_buffer_get_field (_("Send attachment with name: "),
1394                                      fname, MUTT_FILE);
1395         if (ret == 0)
1396         {
1397           /*
1398            * As opposed to RENAME_FILE, we don't check fname[0] because it's
1399            * valid to set an empty string here, to erase what was set
1400            */
1401           mutt_str_replace (&CURATTACH->content->d_filename, mutt_b2s (fname));
1402           menu->redraw = REDRAW_CURRENT;
1403         }
1404       }
1405       break;
1406
1407       case OP_COMPOSE_RENAME_FILE:
1408         CHECK_COUNT;
1409         mutt_buffer_strcpy (fname, CURATTACH->content->filename);
1410         mutt_buffer_pretty_mailbox (fname);
1411
1412         if ((mutt_buffer_get_field (_("Rename to: "), fname, MUTT_FILE) == 0) &&
1413             mutt_buffer_len (fname))
1414         {
1415           if (stat(CURATTACH->content->filename, &st) == -1)
1416           {
1417             /* L10N:
1418                "stat" is a system call. Do "man 2 stat" for more information. */
1419             mutt_error (_("Can't stat %s: %s"), mutt_b2s (fname), strerror (errno));
1420             break;
1421           }
1422
1423           mutt_buffer_expand_path (fname);
1424           if (mutt_rename_file (CURATTACH->content->filename, mutt_b2s (fname)))
1425             break;
1426
1427           mutt_str_replace (&CURATTACH->content->filename, mutt_b2s (fname));
1428           menu->redraw = REDRAW_CURRENT;
1429
1430           if (CURATTACH->content->stamp >= st.st_mtime)
1431             mutt_stamp_attachment(CURATTACH->content);
1432         }
1433
1434         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1435         break;
1436
1437       case OP_COMPOSE_NEW_MIME:
1438       {
1439         char type[STRING];
1440         char *p;
1441         int itype;
1442         FILE *fp;
1443
1444         mutt_window_clearline (MuttMessageWindow, 0);
1445         mutt_buffer_clear (fname);
1446         if ((mutt_buffer_get_field (_("New file: "), fname, MUTT_FILE) != 0) ||
1447             !mutt_buffer_len (fname))
1448           continue;
1449         mutt_buffer_expand_path (fname);
1450
1451         /* Call to lookup_mime_type () ?  maybe later */
1452         type[0] = 0;
1453         if (mutt_get_field ("Content-Type: ", type, sizeof (type), 0) != 0
1454             || !type[0])
1455           continue;
1456
1457         if (!(p = strchr (type, '/')))
1458         {
1459           mutt_error _("Content-Type is of the form base/sub");
1460           continue;
1461         }
1462         *p++ = 0;
1463         if ((itype = mutt_check_mime_type (type)) == TYPEOTHER)
1464         {
1465           mutt_error (_("Unknown Content-Type %s"), type);
1466           continue;
1467         }
1468
1469         new = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
1470         /* Touch the file */
1471         if (!(fp = safe_fopen (mutt_b2s (fname), "w")))
1472         {
1473           mutt_error (_("Can't create file %s"), mutt_b2s (fname));
1474           FREE (&new);
1475           continue;
1476         }
1477         safe_fclose (&fp);
1478
1479         if ((new->content = mutt_make_file_attach (mutt_b2s (fname))) == NULL)
1480         {
1481           mutt_error _("What we have here is a failure to make an attachment");
1482           FREE (&new);
1483           continue;
1484         }
1485         update_idx (menu, actx, new);
1486
1487         CURATTACH->content->type = itype;
1488         mutt_str_replace (&CURATTACH->content->subtype, p);
1489         CURATTACH->content->unlink = 1;
1490         menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
1491
1492         if (mutt_compose_attachment (CURATTACH->content))
1493         {
1494           mutt_update_encoding (CURATTACH->content);
1495           menu->redraw = REDRAW_FULL;
1496         }
1497       }
1498       mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1499       break;
1500
1501       case OP_COMPOSE_EDIT_MIME:
1502         CHECK_COUNT;
1503         if (mutt_edit_attachment (CURATTACH->content))
1504         {
1505           mutt_update_encoding (CURATTACH->content);
1506           menu->redraw = REDRAW_FULL;
1507         }
1508         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1509         break;
1510
1511       case OP_VIEW_ATTACH:
1512       case OP_DISPLAY_HEADERS:
1513         CHECK_COUNT;
1514         mutt_attach_display_loop (menu, op, NULL, actx, 0);
1515         menu->redraw = REDRAW_FULL;
1516         /* no send2hook, since this doesn't modify the message */
1517         break;
1518
1519       case OP_SAVE:
1520         CHECK_COUNT;
1521         mutt_save_attachment_list (actx, NULL, menu->tagprefix, CURATTACH->content, NULL, menu);
1522         /* no send2hook, since this doesn't modify the message */
1523         break;
1524
1525       case OP_PRINT:
1526         CHECK_COUNT;
1527         mutt_print_attachment_list (actx, NULL, menu->tagprefix, CURATTACH->content);
1528         /* no send2hook, since this doesn't modify the message */
1529         break;
1530
1531       case OP_PIPE:
1532       case OP_FILTER:
1533         CHECK_COUNT;
1534         mutt_pipe_attachment_list (actx, NULL, menu->tagprefix, CURATTACH->content, op == OP_FILTER);
1535         if (op == OP_FILTER) /* cte might have changed */
1536           menu->redraw = menu->tagprefix ? REDRAW_FULL : REDRAW_CURRENT;
1537         menu->redraw |= REDRAW_STATUS;
1538         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1539         break;
1540
1541       case OP_EXIT:
1542         if ((i = query_quadoption (OPT_POSTPONE, _("Postpone this message?"))) == MUTT_NO)
1543         {
1544           for (i = 0; i < actx->idxlen; i++)
1545             if (actx->idx[i]->unowned)
1546               actx->idx[i]->content->unlink = 0;
1547
1548           if (!(flags & MUTT_COMPOSE_NOFREEHEADER))
1549           {
1550             for (i = 0; i < actx->idxlen; i++)
1551             {
1552               /* avoid freeing other attachments */
1553               actx->idx[i]->content->next = NULL;
1554               /* See the comment in delete_attachment() */
1555               if (!actx->idx[i]->content->hdr)
1556                 actx->idx[i]->content->parts = NULL;
1557               mutt_free_body (&actx->idx[i]->content);
1558             }
1559           }
1560           r = -1;
1561           loop = 0;
1562           break;
1563         }
1564         else if (i == -1)
1565           break; /* abort */
1566
1567         /* fall through to postpone! */
1568
1569       case OP_COMPOSE_POSTPONE_MESSAGE:
1570
1571         if (check_attachments(actx) != 0)
1572         {
1573           menu->redraw = REDRAW_FULL;
1574           break;
1575         }
1576
1577         loop = 0;
1578         r = 1;
1579         break;
1580
1581       case OP_COMPOSE_ISPELL:
1582         endwin ();
1583         snprintf (buf, sizeof (buf), "%s -x %s", NONULL(Ispell), msg->content->filename);
1584         if (mutt_system (buf) == -1)
1585           mutt_error (_("Error running \"%s\"!"), buf);
1586         else
1587         {
1588           mutt_update_encoding (msg->content);
1589           menu->redraw |= REDRAW_STATUS;
1590         }
1591         break;
1592
1593       case OP_COMPOSE_WRITE_MESSAGE:
1594
1595         mutt_buffer_clear (fname);
1596         if (Context)
1597         {
1598           mutt_buffer_strcpy (fname, NONULL (Context->path));
1599           mutt_buffer_pretty_mailbox (fname);
1600         }
1601         if (actx->idxlen)
1602           msg->content = actx->idx[0]->content;
1603         if ((mutt_buffer_enter_fname (_("Write message to mailbox"), fname,
1604                                       1) != -1) &&
1605             mutt_buffer_len (fname))
1606         {
1607           mutt_message (_("Writing message to %s ..."), mutt_b2s (fname));
1608           mutt_buffer_expand_path (fname);
1609
1610           if (msg->content->next)
1611             msg->content = mutt_make_multipart (msg->content);
1612
1613           if (mutt_write_fcc (mutt_b2s (fname), msg, NULL, 0, NULL) == 0)
1614             mutt_message _("Message written.");
1615
1616           msg->content = mutt_remove_multipart (msg->content);
1617         }
1618         break;
1619
1620
1621
1622       case OP_COMPOSE_PGP_MENU:
1623         if (!(WithCrypto & APPLICATION_PGP))
1624           break;
1625         if (!crypt_has_module_backend (APPLICATION_PGP))
1626         {
1627           mutt_error _("No PGP backend configured");
1628           break;
1629         }
1630         if ((WithCrypto & APPLICATION_SMIME)
1631             && (msg->security & APPLICATION_SMIME))
1632         {
1633           if (msg->security & (ENCRYPT | SIGN))
1634           {
1635             if (mutt_yesorno (_("S/MIME already selected. Clear & continue ? "),
1636                               MUTT_YES) != MUTT_YES)
1637             {
1638               mutt_clear_error ();
1639               break;
1640             }
1641             msg->security &= ~(ENCRYPT | SIGN);
1642           }
1643           msg->security &= ~APPLICATION_SMIME;
1644           msg->security |= APPLICATION_PGP;
1645           update_crypt_info (&rd);
1646         }
1647         msg->security = crypt_pgp_send_menu (msg);
1648         update_crypt_info (&rd);
1649         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1650         break;
1651
1652
1653       case OP_FORGET_PASSPHRASE:
1654         crypt_forget_passphrase ();
1655         break;
1656
1657
1658       case OP_COMPOSE_SMIME_MENU:
1659         if (!(WithCrypto & APPLICATION_SMIME))
1660           break;
1661         if (!crypt_has_module_backend (APPLICATION_SMIME))
1662         {
1663           mutt_error _("No S/MIME backend configured");
1664           break;
1665         }
1666
1667         if ((WithCrypto & APPLICATION_PGP)
1668             && (msg->security & APPLICATION_PGP))
1669         {
1670           if (msg->security & (ENCRYPT | SIGN))
1671           {
1672             if (mutt_yesorno (_("PGP already selected. Clear & continue ? "),
1673                               MUTT_YES) != MUTT_YES)
1674             {
1675               mutt_clear_error ();
1676               break;
1677             }
1678             msg->security &= ~(ENCRYPT | SIGN);
1679           }
1680           msg->security &= ~APPLICATION_PGP;
1681           msg->security |= APPLICATION_SMIME;
1682           update_crypt_info (&rd);
1683         }
1684         msg->security = crypt_smime_send_menu(msg);
1685         update_crypt_info (&rd);
1686         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1687         break;
1688
1689
1690 #ifdef MIXMASTER
1691       case OP_COMPOSE_MIX:
1692
1693         mix_make_chain (&msg->chain);
1694         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1695         break;
1696 #endif
1697
1698 #ifdef USE_AUTOCRYPT
1699       case OP_COMPOSE_AUTOCRYPT_MENU:
1700         if (!option (OPTAUTOCRYPT))
1701           break;
1702
1703         if ((WithCrypto & APPLICATION_SMIME)
1704             && (msg->security & APPLICATION_SMIME))
1705         {
1706           if (msg->security & (ENCRYPT | SIGN))
1707           {
1708             if (mutt_yesorno (_("S/MIME already selected. Clear & continue ? "),
1709                               MUTT_YES) != MUTT_YES)
1710             {
1711               mutt_clear_error ();
1712               break;
1713             }
1714             msg->security &= ~(ENCRYPT | SIGN);
1715           }
1716           msg->security &= ~APPLICATION_SMIME;
1717           msg->security |= APPLICATION_PGP;
1718           update_crypt_info (&rd);
1719         }
1720         autocrypt_compose_menu (msg);
1721         update_crypt_info (&rd);
1722         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
1723         break;
1724 #endif
1725     }
1726   }
1727
1728   mutt_buffer_free (&fname);
1729
1730 #ifdef USE_AUTOCRYPT
1731   /* This is a fail-safe to make sure the bit isn't somehow turned
1732    * on.  The user could have disabled the option after setting AUTOCRYPT,
1733    * or perhaps resuming or replying to an autocrypt message.
1734    */
1735   if (!option (OPTAUTOCRYPT))
1736     msg->security &= ~AUTOCRYPT;
1737 #endif
1738
1739   mutt_pop_current_menu (menu);
1740   mutt_menuDestroy (&menu);
1741
1742   if (actx->idxlen)
1743     msg->content = actx->idx[0]->content;
1744   else
1745     msg->content = NULL;
1746
1747   mutt_free_attach_context (&actx);
1748
1749   return (r);
1750 }