c061c5fcf7bffbe75ef7863dc91c1d102d75b145
[apps/madmutt.git] / compose.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4  * Copyright (C) 2004 g10 Code GmbH
5  *
6  * Parts were written/modified by:
7  * Nico Golde <nico@ngolde.de>
8  *
9  * This file is part of mutt-ng, see http://www.muttng.org/.
10  * It's licensed under the GNU General Public License,
11  * please see the file GPL in the top level source directory.
12  */
13
14 #if HAVE_CONFIG_H
15 # include "config.h"
16 #endif
17
18 #include "mutt.h"
19 #include "enter.h"
20 #include "mutt_curses.h"
21 #include "mutt_idna.h"
22 #include "mutt_menu.h"
23 #include "rfc1524.h"
24 #include "mime.h"
25 #include "attach.h"
26 #include "recvattach.h"
27 #include "mapping.h"
28 #include "sort.h"
29 #include "charset.h"
30 #include "mx.h"
31 #include "buffy.h"
32 #include "compose.h"
33
34 #ifdef MIXMASTER
35 #include "remailer.h"
36 #endif
37
38 #ifdef USE_NNTP
39 #include "nntp.h"
40 #endif
41
42 #include "lib/mem.h"
43 #include "lib/intl.h"
44 #include "lib/str.h"
45
46 #include <errno.h>
47 #include <string.h>
48 #include <sys/stat.h>
49 #include <sys/wait.h>
50 #include <unistd.h>
51 #include <stdlib.h>
52
53 static const char *There_are_no_attachments = N_("There are no attachments.");
54
55 #define CHECK_COUNT if (idxlen == 0) { mutt_error _(There_are_no_attachments); break; }
56
57
58
59 enum {
60   HDR_FROM = 1,
61   HDR_TO,
62   HDR_CC,
63   HDR_BCC,
64   HDR_SUBJECT,
65   HDR_REPLYTO,
66   HDR_FCC,
67
68 #ifdef MIXMASTER
69   HDR_MIX,
70 #endif
71
72   HDR_CRYPT,
73   HDR_CRYPTINFO,
74
75 #ifdef USE_NNTP
76   HDR_NEWSGROUPS,
77   HDR_FOLLOWUPTO,
78   HDR_XCOMMENTTO,
79 #endif
80
81 #ifndef USE_NNTP
82   HDR_ATTACH = (HDR_FCC + 5)    /* where to start printing the attachments */
83 #else
84   HDR_ATTACH = (HDR_FCC + 7)
85 #endif
86 };
87
88 #define HDR_XOFFSET     14
89 #define TITLE_FMT       "%14s"        /* Used for Prompts, which are ASCII */
90 #define SW              (option(OPTMBOXPANE)?SidebarWidth:0)
91 #define W               (COLS - HDR_XOFFSET - SW)
92
93 static char *Prompts[] = {
94   "From: ",
95   "To: ",
96   "Cc: ",
97   "Bcc: ",
98   "Subject: ",
99   "Reply-To: ",
100   "Fcc: "
101 #ifdef USE_NNTP
102 #ifdef MIXMASTER
103     , ""
104 #endif
105     , "", "", "Newsgroups: ", "Followup-To: ", "X-Comment-To: "
106 #endif
107 };
108
109 static struct mapping_t ComposeHelp[] = {
110   {N_("Send"), OP_COMPOSE_SEND_MESSAGE},
111   {N_("Abort"), OP_EXIT},
112   {"To", OP_COMPOSE_EDIT_TO},
113   {"CC", OP_COMPOSE_EDIT_CC},
114   {"Subj", OP_COMPOSE_EDIT_SUBJECT},
115   {N_("Attach file"), OP_COMPOSE_ATTACH_FILE},
116   {N_("Descrip"), OP_COMPOSE_EDIT_DESCRIPTION},
117   {N_("Help"), OP_HELP},
118   {NULL}
119 };
120
121 #ifdef USE_NNTP
122 static struct mapping_t ComposeNewsHelp[] = {
123   {N_("Send"), OP_COMPOSE_SEND_MESSAGE},
124   {N_("Abort"), OP_EXIT},
125   {"Newsgroups", OP_COMPOSE_EDIT_NEWSGROUPS},
126   {"Subj", OP_COMPOSE_EDIT_SUBJECT},
127   {N_("Attach file"), OP_COMPOSE_ATTACH_FILE},
128   {N_("Descrip"), OP_COMPOSE_EDIT_DESCRIPTION},
129   {N_("Help"), OP_HELP},
130   {NULL}
131 };
132 #endif
133
134 static void snd_entry (char *b, size_t blen, MUTTMENU * menu, int num)
135 {
136   int old1=DrawFullLine,old2=option(OPTSTATUSONTOP);
137
138   /*
139    * XXX
140    * mutt_FormatString is totally broken as it tries to determine
141    * the max. width of the output string itself which may fail
142    * (for example, for $status_on_top, many of the format strings
143    * are as wide as screen but $attach_format is not, etc.)
144    *
145    * When $status_on_top is unset, mutt_FormatString() will respect
146    * sidebar so unset it for compose menu entries by force... ;-(
147    */
148   DrawFullLine=0;
149   unset_option(OPTSTATUSONTOP);
150
151   mutt_FormatString (b, blen, NONULL (AttachFormat), mutt_attach_fmt,
152                      (unsigned long) (((ATTACHPTR **) menu->data)[num]),
153                      M_FORMAT_STAT_FILE | M_FORMAT_ARROWCURSOR);
154
155   DrawFullLine=old1;
156   if (old2) set_option(OPTSTATUSONTOP);
157 }
158
159
160
161 #include "mutt_crypt.h"
162
163 static void redraw_crypt_lines (HEADER * msg)
164 {
165   int off = 0;
166
167   if ((WithCrypto & APPLICATION_PGP) && (WithCrypto & APPLICATION_SMIME)) {
168     if (!msg->security)
169       mvaddstr (HDR_CRYPT, SW, "    Security: ");
170     else if (msg->security & APPLICATION_SMIME)
171       mvaddstr (HDR_CRYPT, SW, "      S/MIME: ");
172     else if (msg->security & APPLICATION_PGP)
173       mvaddstr (HDR_CRYPT, SW, "         PGP: ");
174   }
175   else if ((WithCrypto & APPLICATION_SMIME))
176     mvaddstr (HDR_CRYPT, SW, "      S/MIME: ");
177   else if ((WithCrypto & APPLICATION_PGP))
178     mvaddstr (HDR_CRYPT, SW, "         PGP: ");
179   else
180     return;
181
182   if ((msg->security & (ENCRYPT | SIGN)) == (ENCRYPT | SIGN))
183     addstr (_("Sign, Encrypt"));
184   else if (msg->security & ENCRYPT)
185     addstr (_("Encrypt"));
186   else if (msg->security & SIGN)
187     addstr (_("Sign"));
188   else
189     addstr (_("Clear"));
190
191   if ((WithCrypto & APPLICATION_PGP))
192     if ((msg->security & APPLICATION_PGP)
193         && (msg->security & (ENCRYPT | SIGN))) {
194       if ((msg->security & INLINE))
195         addstr (_(" (inline)"));
196       else
197         addstr (_(" (PGP/MIME)"));
198     }
199   clrtoeol ();
200
201   move (HDR_CRYPTINFO, SW);
202   clrtoeol ();
203   if ((WithCrypto & APPLICATION_PGP)
204       && msg->security & APPLICATION_PGP && msg->security & SIGN)
205     printw ("%s%s", _("     sign as: "),
206             PgpSignAs ? PgpSignAs : _("<default>"));
207
208   if ((WithCrypto & APPLICATION_SMIME)
209       && msg->security & APPLICATION_SMIME && msg->security & SIGN) {
210     printw ("%s%s", _("     sign as: "),
211             SmimeDefaultKey ? SmimeDefaultKey : _("<default>"));
212   }
213
214   if ((WithCrypto & APPLICATION_SMIME)
215       && (msg->security & APPLICATION_SMIME)
216       && (msg->security & ENCRYPT)
217       && SmimeCryptAlg && *SmimeCryptAlg) {
218     mvprintw (HDR_CRYPTINFO, SW + 40, "%s%s", _("Encrypt with: "),
219               NONULL (SmimeCryptAlg));
220     off = 20;
221   }
222 }
223
224
225 #ifdef MIXMASTER
226
227 static void redraw_mix_line (LIST * chain)
228 {
229   int c;
230   char *t;
231
232   mvaddstr (HDR_MIX, SW, "         Mix: ");
233
234   if (!chain) {
235     addstr ("<no chain defined>");
236     clrtoeol ();
237     return;
238   }
239
240   for (c = 12; chain; chain = chain->next) {
241     t = chain->data;
242     if (t && t[0] == '0' && t[1] == '\0')
243       t = "<random>";
244
245     if (c + str_len (t) + 2 >= COLS - SW)
246       break;
247
248     addstr (NONULL (t));
249     if (chain->next)
250       addstr (", ");
251
252     c += str_len (t) + 2;
253   }
254 }
255 #endif /* MIXMASTER */
256
257 static int check_attachments (ATTACHPTR ** idx, short idxlen)
258 {
259   int i, r;
260   struct stat st;
261   char pretty[_POSIX_PATH_MAX], msg[_POSIX_PATH_MAX + SHORT_STRING];
262
263   for (i = 0; i < idxlen; i++) {
264     strfcpy (pretty, idx[i]->content->filename, sizeof (pretty));
265     if (stat (idx[i]->content->filename, &st) != 0) {
266       mutt_pretty_mailbox (pretty);
267       mutt_error (_("%s [#%d] no longer exists!"), pretty, i + 1);
268       return -1;
269     }
270
271     if (idx[i]->content->stamp < st.st_mtime) {
272       mutt_pretty_mailbox (pretty);
273       snprintf (msg, sizeof (msg), _("%s [#%d] modified. Update encoding?"),
274                 pretty, i + 1);
275
276       if ((r = mutt_yesorno (msg, M_YES)) == M_YES)
277         mutt_update_encoding (idx[i]->content);
278       else if (r == -1)
279         return -1;
280     }
281   }
282
283   return 0;
284 }
285
286 static void draw_envelope_addr (int line, ADDRESS * addr)
287 {
288   char buf[STRING];
289
290   buf[0] = 0;
291   rfc822_write_address (buf, sizeof (buf), addr, 1);
292   mvprintw (line, SW, TITLE_FMT, Prompts[line - 1]);
293   mutt_paddstr (W, buf);
294 }
295
296 static void draw_envelope (HEADER * msg, char *fcc)
297 {
298   draw_envelope_addr (HDR_FROM, msg->env->from);
299 #ifdef USE_NNTP
300   if (!option (OPTNEWSSEND)) {
301 #endif
302     draw_envelope_addr (HDR_TO, msg->env->to);
303     draw_envelope_addr (HDR_CC, msg->env->cc);
304     draw_envelope_addr (HDR_BCC, msg->env->bcc);
305 #ifdef USE_NNTP
306   }
307   else {
308     mvprintw (HDR_TO, SW, TITLE_FMT, Prompts[HDR_NEWSGROUPS - 1]);
309     mutt_paddstr (W, NONULL (msg->env->newsgroups));
310     mvprintw (HDR_CC, SW, TITLE_FMT, Prompts[HDR_FOLLOWUPTO - 1]);
311     mutt_paddstr (W, NONULL (msg->env->followup_to));
312     if (option (OPTXCOMMENTTO)) {
313       mvprintw (HDR_BCC, 0, TITLE_FMT, Prompts[HDR_XCOMMENTTO - 1]);
314       mutt_paddstr (W, NONULL (msg->env->x_comment_to));
315     }
316   }
317 #endif
318   mvprintw (HDR_SUBJECT, SW, TITLE_FMT, Prompts[HDR_SUBJECT - 1]);
319   mutt_paddstr (W, NONULL (msg->env->subject));
320   draw_envelope_addr (HDR_REPLYTO, msg->env->reply_to);
321   mvprintw (HDR_FCC, SW, TITLE_FMT, Prompts[HDR_FCC - 1]);
322   mutt_paddstr (W, fcc);
323
324   if (WithCrypto)
325     redraw_crypt_lines (msg);
326
327 #ifdef MIXMASTER
328   redraw_mix_line (msg->chain);
329 #endif
330
331   SETCOLOR (MT_COLOR_STATUS);
332   mvaddstr (HDR_ATTACH - 1, SW, _("-- Attachments"));
333   BKGDSET (MT_COLOR_STATUS);
334   clrtoeol ();
335
336   BKGDSET (MT_COLOR_NORMAL);
337   SETCOLOR (MT_COLOR_NORMAL);
338 }
339
340 static int edit_address_list (int line, ADDRESS ** addr)
341 {
342   char buf[HUGE_STRING] = "";   /* needs to be large for alias expansion */
343   char *err = NULL;
344
345   mutt_addrlist_to_local (*addr);
346   rfc822_write_address (buf, sizeof (buf), *addr, 0);
347   if (mutt_get_field (Prompts[line - 1], buf, sizeof (buf), M_ALIAS) == 0) {
348     rfc822_free_address (addr);
349     *addr = mutt_parse_adrlist (*addr, buf);
350     *addr = mutt_expand_aliases (*addr);
351   }
352
353   if (option (OPTNEEDREDRAW)) {
354     unset_option (OPTNEEDREDRAW);
355     return (REDRAW_FULL);
356   }
357
358   if (mutt_addrlist_to_idna (*addr, &err) != 0) {
359     mutt_error (_("Warning: '%s' is a bad IDN."), err);
360     mutt_refresh ();
361     mem_free (&err);
362   }
363
364   /* redraw the expanded list so the user can see the result */
365   buf[0] = 0;
366   rfc822_write_address (buf, sizeof (buf), *addr, 1);
367   move (line, HDR_XOFFSET + SW);
368   mutt_paddstr (W, buf);
369
370   return 0;
371 }
372
373 static int delete_attachment (MUTTMENU * menu, short *idxlen, int x)
374 {
375   ATTACHPTR **idx = (ATTACHPTR **) menu->data;
376   int y;
377
378   menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
379
380   if (x == 0 && menu->max == 1) {
381     mutt_error _("You may not delete the only attachment.");
382
383     idx[x]->content->tagged = 0;
384     return (-1);
385   }
386
387   for (y = 0; y < *idxlen; y++) {
388     if (idx[y]->content->next == idx[x]->content) {
389       idx[y]->content->next = idx[x]->content->next;
390       break;
391     }
392   }
393
394   idx[x]->content->next = NULL;
395   idx[x]->content->parts = NULL;
396   mutt_free_body (&(idx[x]->content));
397   mem_free (&idx[x]->tree);
398   mem_free (&idx[x]);
399   for (; x < *idxlen - 1; x++)
400     idx[x] = idx[x + 1];
401   menu->max = --(*idxlen);
402
403   return (0);
404 }
405
406 static void update_idx (MUTTMENU * menu, ATTACHPTR ** idx, short idxlen)
407 {
408   idx[idxlen]->level = (idxlen > 0) ? idx[idxlen - 1]->level : 0;
409   if (idxlen)
410     idx[idxlen - 1]->content->next = idx[idxlen]->content;
411   idx[idxlen]->content->aptr = idx[idxlen];
412   menu->current = idxlen++;
413   mutt_update_tree (idx, idxlen);
414   menu->max = idxlen;
415   return;
416 }
417
418
419 /* 
420  * cum_attachs_size: Cumulative Attachments Size
421  *
422  * Returns the total number of bytes used by the attachments in the
423  * attachment list _after_ content-transfer-encodings have been
424  * applied.
425  * 
426  */
427
428 static unsigned long cum_attachs_size (MUTTMENU * menu)
429 {
430   size_t s;
431   unsigned short i;
432   ATTACHPTR **idx = menu->data;
433   CONTENT *info;
434   BODY *b;
435
436   for (i = 0, s = 0; i < menu->max; i++) {
437     b = idx[i]->content;
438
439     if (!b->content)
440       b->content = mutt_get_content_info (b->filename, b);
441
442     if ((info = b->content)) {
443       switch (b->encoding) {
444       case ENCQUOTEDPRINTABLE:
445         s += 3 * (info->lobin + info->hibin) + info->ascii + info->crlf;
446         break;
447       case ENCBASE64:
448         s += (4 * (info->lobin + info->hibin + info->ascii + info->crlf)) / 3;
449         break;
450       default:
451         s += info->lobin + info->hibin + info->ascii + info->crlf;
452         break;
453       }
454     }
455   }
456
457   return s;
458 }
459
460 /*
461  * compose_format_str()
462  *
463  * %a = total number of attachments 
464  * %h = hostname  [option]
465  * %l = approx. length of current message (in bytes) 
466  * %v = Mutt version 
467  *
468  * This function is similar to status_format_str().  Look at that function for
469  * help when modifying this function.
470  */
471
472 static const char *compose_format_str (char *buf, size_t buflen, char op,
473                                        const char *src, const char *prefix,
474                                        const char *ifstring,
475                                        const char *elsestring,
476                                        unsigned long data, format_flag flags)
477 {
478   char fmt[SHORT_STRING], tmp[SHORT_STRING];
479   int optional = (flags & M_FORMAT_OPTIONAL);
480   MUTTMENU *menu = (MUTTMENU *) data;
481
482   *buf = 0;
483   switch (op) {
484   case 'a':                    /* total number of attachments */
485     snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
486     snprintf (buf, buflen, fmt, menu->max);
487     break;
488
489   case 'h':                    /* hostname */
490     snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
491     snprintf (buf, buflen, fmt, NONULL (Hostname));
492     break;
493
494   case 'l':                    /* approx length of current message in bytes */
495     snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
496     mutt_pretty_size (tmp, sizeof (tmp), menu ? cum_attachs_size (menu) : 0);
497     snprintf (buf, buflen, fmt, tmp);
498     break;
499
500   case 'v':
501     strncpy (buf, mutt_make_version (0), buflen);
502     break;
503
504   case 0:
505     *buf = 0;
506     return (src);
507
508   default:
509     snprintf (buf, buflen, "%%%s%c", prefix, op);
510     break;
511   }
512
513   if (optional)
514     compose_status_line (buf, buflen, menu, ifstring);
515   else if (flags & M_FORMAT_OPTIONAL)
516     compose_status_line (buf, buflen, menu, elsestring);
517
518   return (src);
519 }
520
521 static void compose_status_line (char *buf, size_t buflen, MUTTMENU * menu,
522                                  const char *p)
523 {
524   mutt_FormatString (buf, buflen, p, compose_format_str,
525                      (unsigned long) menu, 0);
526 }
527
528
529 /* return values:
530  *
531  * 1    message should be postponed
532  * 0    normal exit
533  * -1   abort message
534  */
535 int mutt_compose_menu (HEADER * msg,    /* structure for new message */
536                        char *fcc,       /* where to save a copy of the message */
537                        size_t fcclen, HEADER * cur)
538 {                               /* current message */
539   char helpstr[SHORT_STRING];
540   char buf[LONG_STRING];
541   char fname[_POSIX_PATH_MAX];
542   MUTTMENU *menu;
543   ATTACHPTR **idx = NULL;
544   short idxlen = 0;
545   short idxmax = 0;
546   int i, close = 0;
547   int r = -1;                   /* return value */
548   int op = 0;
549   int loop = 1;
550   int fccSet = 0;               /* has the user edited the Fcc: field ? */
551   CONTEXT *ctx = NULL, *this = NULL;
552
553   /* Sort, SortAux could be changed in mutt_index_menu() */
554   int oldSort, oldSortAux;
555   struct stat st;
556
557 #ifdef USE_NNTP
558   int news = 0;                 /* is it a news article ? */
559
560   if (option (OPTNEWSSEND))
561     news++;
562 #endif
563
564   mutt_attach_init (msg->content);
565   idx = mutt_gen_attach_list (msg->content, -1, idx, &idxlen, &idxmax, 0, 1);
566
567   menu = mutt_new_menu ();
568   menu->menu = MENU_COMPOSE;
569   menu->offset = HDR_ATTACH;
570   menu->max = idxlen;
571   menu->make_entry = snd_entry;
572   menu->tag = mutt_tag_attach;
573   menu->data = idx;
574 #ifdef USE_NNTP
575   if (news)
576     menu->help =
577       mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE,
578                          ComposeNewsHelp);
579   else
580 #endif
581     menu->help =
582       mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE,
583                          ComposeHelp);
584
585   if (option (OPTMBOXPANE))
586     buffy_check (0);
587   while (loop) {
588 #ifdef USE_NNTP
589     unset_option (OPTNEWS);     /* for any case */
590 #endif
591     switch (op = mutt_menuLoop (menu)) {
592     case OP_REDRAW:
593       draw_envelope (msg, fcc);
594       menu->offset = HDR_ATTACH;
595       menu->pagelen = LINES - HDR_ATTACH - 2;
596       break;
597     case OP_COMPOSE_EDIT_FROM:
598       menu->redraw = edit_address_list (HDR_FROM, &msg->env->from);
599       mutt_message_hook (NULL, msg, M_SEND2HOOK);
600       break;
601     case OP_COMPOSE_EDIT_TO:
602 #ifdef USE_NNTP
603       if (!news) {
604 #endif
605         menu->redraw = edit_address_list (HDR_TO, &msg->env->to);
606         mutt_message_hook (NULL, msg, M_SEND2HOOK);
607 #ifdef USE_NNTP
608       }
609 #endif
610       break;
611     case OP_COMPOSE_EDIT_BCC:
612 #ifdef USE_NNTP
613       if (!news) {
614 #endif
615         menu->redraw = edit_address_list (HDR_BCC, &msg->env->bcc);
616         mutt_message_hook (NULL, msg, M_SEND2HOOK);
617 #ifdef USE_NNTP
618       }
619 #endif
620       break;
621     case OP_COMPOSE_EDIT_CC:
622 #ifdef USE_NNTP
623       if (!news) {
624 #endif
625         menu->redraw = edit_address_list (HDR_CC, &msg->env->cc);
626         mutt_message_hook (NULL, msg, M_SEND2HOOK);
627 #ifdef USE_NNTP
628       }
629 #endif
630       break;
631 #ifdef USE_NNTP
632     case OP_COMPOSE_EDIT_NEWSGROUPS:
633       if (news) {
634         if (msg->env->newsgroups)
635           strfcpy (buf, msg->env->newsgroups, sizeof (buf));
636         else
637           buf[0] = 0;
638         if (mutt_get_field ("Newsgroups: ", buf, sizeof (buf), 0) == 0
639             && buf[0]) {
640           mem_free (&msg->env->newsgroups);
641           str_skip_trailws (buf);
642           msg->env->newsgroups = str_dup (str_skip_initws (buf));
643           move (HDR_TO, HDR_XOFFSET);
644           clrtoeol ();
645           if (msg->env->newsgroups)
646             printw ("%-*.*s", W, W, msg->env->newsgroups);
647         }
648       }
649       break;
650
651     case OP_COMPOSE_EDIT_FOLLOWUP_TO:
652       if (news) {
653         buf[0] = 0;
654         if (msg->env->followup_to)
655           strfcpy (buf, msg->env->followup_to, sizeof (buf));
656         if (mutt_get_field ("Followup-To: ", buf, sizeof (buf), 0) == 0
657             && buf[0]) {
658           mem_free (&msg->env->followup_to);
659           str_skip_trailws (buf);
660           msg->env->followup_to = str_dup (str_skip_initws (buf));
661           move (HDR_CC, HDR_XOFFSET);
662           clrtoeol ();
663           if (msg->env->followup_to)
664             printw ("%-*.*s", W, W, msg->env->followup_to);
665         }
666       }
667       break;
668
669     case OP_COMPOSE_EDIT_X_COMMENT_TO:
670       if (news && option (OPTXCOMMENTTO)) {
671         buf[0] = 0;
672         if (msg->env->x_comment_to)
673           strfcpy (buf, msg->env->x_comment_to, sizeof (buf));
674         if (mutt_get_field ("X-Comment-To: ", buf, sizeof (buf), 0) == 0
675             && buf[0]) {
676           mem_free (&msg->env->x_comment_to);
677           msg->env->x_comment_to = str_dup (buf);
678           move (HDR_BCC, HDR_XOFFSET);
679           clrtoeol ();
680           if (msg->env->x_comment_to)
681             printw ("%-*.*s", W, W, msg->env->x_comment_to);
682         }
683       }
684       break;
685 #endif
686     case OP_COMPOSE_EDIT_SUBJECT:
687       if (msg->env->subject)
688         strfcpy (buf, msg->env->subject, sizeof (buf));
689       else
690         buf[0] = 0;
691       if (mutt_get_field ("Subject: ", buf, sizeof (buf), 0) == 0) {
692         str_replace (&msg->env->subject, buf);
693         move (HDR_SUBJECT, HDR_XOFFSET + SW);
694         clrtoeol ();
695         if (msg->env->subject)
696           mutt_paddstr (W, msg->env->subject);
697       }
698       mutt_message_hook (NULL, msg, M_SEND2HOOK);
699       break;
700     case OP_COMPOSE_EDIT_REPLY_TO:
701       menu->redraw = edit_address_list (HDR_REPLYTO, &msg->env->reply_to);
702       mutt_message_hook (NULL, msg, M_SEND2HOOK);
703       break;
704     case OP_COMPOSE_EDIT_FCC:
705       strfcpy (buf, fcc, sizeof (buf));
706       if (mutt_get_field ("Fcc: ", buf, sizeof (buf), M_FILE | M_CLEAR) == 0) {
707         strfcpy (fcc, buf, _POSIX_PATH_MAX);
708         mutt_pretty_mailbox (fcc);
709         move (HDR_FCC, HDR_XOFFSET + SW);
710         mutt_paddstr (W, fcc);
711         fccSet = 1;
712       }
713       MAYBE_REDRAW (menu->redraw);
714       mutt_message_hook (NULL, msg, M_SEND2HOOK);
715       break;
716     case OP_COMPOSE_EDIT_MESSAGE:
717       if (Editor && (str_cmp ("builtin", Editor) != 0)
718           && !option (OPTEDITHDRS)) {
719         mutt_edit_file (Editor, msg->content->filename);
720         mutt_update_encoding (msg->content);
721         menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
722         mutt_message_hook (NULL, msg, M_SEND2HOOK);
723         break;
724       }
725       /* fall through */
726     case OP_COMPOSE_EDIT_HEADERS:
727       if (str_cmp ("builtin", Editor) != 0 &&
728           (op == OP_COMPOSE_EDIT_HEADERS ||
729            (op == OP_COMPOSE_EDIT_MESSAGE && option (OPTEDITHDRS)))) {
730         char *tag = NULL, *err = NULL;
731
732         mutt_env_to_local (msg->env);
733         mutt_edit_headers (NONULL (Editor), msg->content->filename, msg,
734                            fcc, fcclen);
735         if (mutt_env_to_idna (msg->env, &tag, &err)) {
736           mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err);
737           mem_free (&err);
738         }
739       }
740       else {
741         /* this is grouped with OP_COMPOSE_EDIT_HEADERS because the
742            attachment list could change if the user invokes ~v to edit
743            the message with headers, in which we need to execute the
744            code below to regenerate the index array */
745         mutt_builtin_editor (msg->content->filename, msg, cur);
746       }
747       mutt_update_encoding (msg->content);
748
749       /* attachments may have been added */
750       if (idxlen && idx[idxlen - 1]->content->next) {
751         for (i = 0; i < idxlen; i++)
752           mem_free (&idx[i]);
753         idxlen = 0;
754         idx =
755           mutt_gen_attach_list (msg->content, -1, idx, &idxlen, &idxmax, 0,
756                                 1);
757         menu->data = idx;
758         menu->max = idxlen;
759       }
760
761       menu->redraw = REDRAW_FULL;
762       mutt_message_hook (NULL, msg, M_SEND2HOOK);
763       break;
764
765
766
767     case OP_COMPOSE_ATTACH_KEY:
768       if (!(WithCrypto & APPLICATION_PGP))
769         break;
770       if (idxlen == idxmax) {
771         mem_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5));
772         menu->data = idx;
773       }
774
775       idx[idxlen] = (ATTACHPTR *) mem_calloc (1, sizeof (ATTACHPTR));
776       if ((idx[idxlen]->content =
777            crypt_pgp_make_key_attachment (NULL)) != NULL) {
778         update_idx (menu, idx, idxlen++);
779         menu->redraw |= REDRAW_INDEX;
780       }
781       else
782         mem_free (&idx[idxlen]);
783
784       menu->redraw |= REDRAW_STATUS;
785
786       if (option (OPTNEEDREDRAW)) {
787         menu->redraw = REDRAW_FULL;
788         unset_option (OPTNEEDREDRAW);
789       }
790
791       mutt_message_hook (NULL, msg, M_SEND2HOOK);
792       break;
793
794
795     case OP_COMPOSE_ATTACH_FILE:
796       {
797         char *prompt, **files;
798         int error, numfiles;
799
800         fname[0] = 0;
801         prompt = _("Attach file");
802         numfiles = 0;
803         files = NULL;
804
805         if (_mutt_enter_fname
806             (prompt, fname, sizeof (fname), &menu->redraw, 0, 1, &files,
807              &numfiles) == -1 || *fname == '\0')
808           break;
809
810         if (idxlen + numfiles >= idxmax) {
811           mem_realloc (&idx,
812                         sizeof (ATTACHPTR *) * (idxmax += 5 + numfiles));
813           menu->data = idx;
814         }
815
816         error = 0;
817         if (numfiles > 1)
818           mutt_message _("Attaching selected files...");
819
820         for (i = 0; i < numfiles; i++) {
821           char *att = files[i];
822
823           idx[idxlen] = (ATTACHPTR *) mem_calloc (1, sizeof (ATTACHPTR));
824           idx[idxlen]->unowned = 1;
825           idx[idxlen]->content = mutt_make_file_attach (att);
826           if (idx[idxlen]->content != NULL)
827             update_idx (menu, idx, idxlen++);
828           else {
829             error = 1;
830             mutt_error (_("Unable to attach %s!"), att);
831             mem_free (&idx[idxlen]);
832           }
833         }
834
835         mem_free (&files);
836         if (!error)
837           mutt_clear_error ();
838
839         menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
840       }
841       mutt_message_hook (NULL, msg, M_SEND2HOOK);
842       break;
843
844     case OP_COMPOSE_ATTACH_MESSAGE:
845 #ifdef USE_NNTP
846     case OP_COMPOSE_ATTACH_NEWS_MESSAGE:
847 #endif
848       {
849         char *prompt;
850         HEADER *h;
851
852         fname[0] = 0;
853         prompt = _("Open mailbox to attach message from");
854
855 #ifdef USE_NNTP
856         unset_option (OPTNEWS);
857         if (op == OP_COMPOSE_ATTACH_NEWS_MESSAGE) {
858           if (!(CurrentNewsSrv = mutt_select_newsserver (NewsServer)))
859             break;
860
861           prompt = _("Open newsgroup to attach message from");
862           set_option (OPTNEWS);
863         }
864 #endif
865
866         if (Context)
867 #ifdef USE_NNTP
868           if ((op == OP_COMPOSE_ATTACH_MESSAGE) ^ (Context->magic == M_NNTP))
869 #endif
870           {
871             strfcpy (fname, NONULL (Context->path), sizeof (fname));
872             mutt_pretty_mailbox (fname);
873           }
874
875         if (mutt_enter_fname (prompt, fname, sizeof (fname), &menu->redraw, 1)
876             == -1 || !fname[0])
877           break;
878
879 #ifdef USE_NNTP
880         if (option (OPTNEWS))
881           nntp_expand_path (fname, sizeof (fname),
882                             &CurrentNewsSrv->conn->account);
883         else
884 #endif
885           mutt_expand_path (fname, sizeof (fname));
886 #ifdef USE_IMAP
887         if (mx_get_magic (fname) != M_IMAP)
888 #endif
889 #ifdef USE_POP
890           if (mx_get_magic (fname) != M_POP)
891 #endif
892 #ifdef USE_NNTP
893             if (mx_get_magic (fname) != M_NNTP && !option (OPTNEWS))
894 #endif
895               /* check to make sure the file exists and is readable */
896               if (access (fname, R_OK) == -1) {
897                 mutt_perror (fname);
898                 break;
899               }
900
901         menu->redraw = REDRAW_FULL;
902
903         ctx = mx_open_mailbox (fname, M_READONLY, NULL);
904         if (ctx == NULL) {
905           mutt_perror (fname);
906           break;
907         }
908
909         if (!ctx->msgcount) {
910           mx_close_mailbox (ctx, NULL);
911           mem_free (&ctx);
912           mutt_error _("No messages in that folder.");
913
914           break;
915         }
916
917         this = Context;         /* remember current folder and sort methods */
918         oldSort = Sort;
919         oldSortAux = SortAux;
920
921         Context = ctx;
922         set_option (OPTATTACHMSG);
923         mutt_message _("Tag the messages you want to attach!");
924
925         close = mutt_index_menu ();
926         unset_option (OPTATTACHMSG);
927
928         if (!Context) {
929           /* go back to the folder we started from */
930           Context = this;
931           /* Restore old $sort and $sort_aux */
932           Sort = oldSort;
933           SortAux = oldSortAux;
934           menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
935           break;
936         }
937
938         if (idxlen + Context->tagged >= idxmax) {
939           mem_realloc (&idx,
940                         sizeof (ATTACHPTR *) * (idxmax +=
941                                                 5 + Context->tagged));
942           menu->data = idx;
943         }
944
945         for (i = 0; i < Context->msgcount; i++) {
946           h = Context->hdrs[i];
947           if (h->tagged) {
948             idx[idxlen] = (ATTACHPTR *) mem_calloc (1, sizeof (ATTACHPTR));
949             idx[idxlen]->content = mutt_make_message_attach (Context, h, 1);
950             if (idx[idxlen]->content != NULL)
951               update_idx (menu, idx, idxlen++);
952             else {
953               mutt_error _("Unable to attach!");
954
955               mem_free (&idx[idxlen]);
956             }
957           }
958         }
959         menu->redraw |= REDRAW_FULL;
960
961         if (close == OP_QUIT)
962           mx_close_mailbox (Context, NULL);
963         else
964           mx_fastclose_mailbox (Context);
965         mem_free (&Context);
966
967         /* go back to the folder we started from */
968         Context = this;
969         /* Restore old $sort and $sort_aux */
970         Sort = oldSort;
971         SortAux = oldSortAux;
972       }
973       mutt_message_hook (NULL, msg, M_SEND2HOOK);
974       break;
975
976     case OP_DELETE:
977       CHECK_COUNT;
978       if (idx[menu->current]->unowned)
979         idx[menu->current]->content->unlink = 0;
980       if (delete_attachment (menu, &idxlen, menu->current) == -1)
981         break;
982       mutt_update_tree (idx, idxlen);
983       if (idxlen) {
984         if (menu->current > idxlen - 1)
985           menu->current = idxlen - 1;
986       }
987       else
988         menu->current = 0;
989
990       if (menu->current == 0)
991         msg->content = idx[0]->content;
992
993       menu->redraw |= REDRAW_STATUS;
994       mutt_message_hook (NULL, msg, M_SEND2HOOK);
995       break;
996
997 #define CURRENT idx[menu->current]->content
998
999     case OP_COMPOSE_TOGGLE_RECODE:
1000       {
1001         CHECK_COUNT;
1002         if (!mutt_is_text_part (CURRENT)) {
1003           mutt_error (_("Recoding only affects text attachments."));
1004           break;
1005         }
1006         CURRENT->noconv = !CURRENT->noconv;
1007         if (CURRENT->noconv)
1008           mutt_message (_("The current attachment won't be converted."));
1009         else
1010           mutt_message (_("The current attachment will be converted."));
1011         menu->redraw = REDRAW_CURRENT;
1012         mutt_message_hook (NULL, msg, M_SEND2HOOK);
1013         break;
1014       }
1015 #undef CURRENT
1016
1017     case OP_COMPOSE_EDIT_DESCRIPTION:
1018       CHECK_COUNT;
1019       strfcpy (buf,
1020                idx[menu->current]->content->description ?
1021                idx[menu->current]->content->description : "", sizeof (buf));
1022       /* header names should not be translated */
1023       if (mutt_get_field ("Description: ", buf, sizeof (buf), 0) == 0) {
1024         str_replace (&idx[menu->current]->content->description, buf);
1025         menu->redraw = REDRAW_CURRENT;
1026       }
1027       mutt_message_hook (NULL, msg, M_SEND2HOOK);
1028       break;
1029
1030     case OP_COMPOSE_UPDATE_ENCODING:
1031       CHECK_COUNT;
1032       if (menu->tagprefix) {
1033         BODY *top;
1034
1035         for (top = msg->content; top; top = top->next) {
1036           if (top->tagged)
1037             mutt_update_encoding (top);
1038         }
1039         menu->redraw = REDRAW_FULL;
1040       }
1041       else {
1042         mutt_update_encoding (idx[menu->current]->content);
1043         menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
1044       }
1045       mutt_message_hook (NULL, msg, M_SEND2HOOK);
1046       break;
1047
1048     case OP_COMPOSE_TOGGLE_DISPOSITION:
1049       /* toggle the content-disposition between inline/attachment */
1050       idx[menu->current]->content->disposition =
1051         (idx[menu->current]->content->disposition ==
1052          DISPINLINE) ? DISPATTACH : DISPINLINE;
1053       menu->redraw = REDRAW_CURRENT;
1054       break;
1055
1056     case OP_EDIT_TYPE:
1057       CHECK_COUNT;
1058       {
1059         mutt_edit_content_type (NULL, idx[menu->current]->content, NULL);
1060
1061         /* this may have been a change to text/something */
1062         mutt_update_encoding (idx[menu->current]->content);
1063
1064         menu->redraw = REDRAW_CURRENT;
1065       }
1066       mutt_message_hook (NULL, msg, M_SEND2HOOK);
1067       break;
1068
1069     case OP_COMPOSE_EDIT_ENCODING:
1070       CHECK_COUNT;
1071       strfcpy (buf, ENCODING (idx[menu->current]->content->encoding),
1072                sizeof (buf));
1073       if (mutt_get_field ("Content-Transfer-Encoding: ", buf,
1074                           sizeof (buf), 0) == 0 && buf[0]) {
1075         if ((i = mutt_check_encoding (buf)) != ENCOTHER && i != ENCUUENCODED) {
1076           idx[menu->current]->content->encoding = i;
1077           menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
1078           mutt_clear_error ();
1079         }
1080         else
1081           mutt_error _("Invalid encoding.");
1082       }
1083       mutt_message_hook (NULL, msg, M_SEND2HOOK);
1084       break;
1085
1086     case OP_COMPOSE_SEND_MESSAGE:
1087
1088       /* Note: We don't invoke send2-hook here, since we want to leave
1089        * users an opportunity to change settings from the ":" prompt.
1090        */
1091
1092       if (check_attachments (idx, idxlen) != 0) {
1093         menu->redraw = REDRAW_FULL;
1094         break;
1095       }
1096
1097
1098 #ifdef MIXMASTER
1099       if (msg->chain && mix_check_message (msg) != 0)
1100         break;
1101 #endif
1102
1103       if (!fccSet && *fcc) {
1104         if ((i = query_quadoption (OPT_COPY,
1105                                    _("Save a copy of this message?"))) == -1)
1106           break;
1107         else if (i == M_NO)
1108           *fcc = 0;
1109       }
1110
1111       loop = 0;
1112       r = 0;
1113       break;
1114
1115     case OP_COMPOSE_EDIT_FILE:
1116       CHECK_COUNT;
1117       mutt_edit_file (NONULL (Editor), idx[menu->current]->content->filename);
1118       mutt_update_encoding (idx[menu->current]->content);
1119       menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
1120       mutt_message_hook (NULL, msg, M_SEND2HOOK);
1121       break;
1122
1123     case OP_COMPOSE_TOGGLE_UNLINK:
1124       CHECK_COUNT;
1125       idx[menu->current]->content->unlink =
1126         !idx[menu->current]->content->unlink;
1127
1128 #if 0
1129       /* OPTRESOLVE is otherwise ignored on this menu.
1130        * Where's the bug?
1131        */
1132
1133       if (option (OPTRESOLVE) && menu->current + 1 < menu->max)
1134         menu->current++;
1135 # endif
1136       menu->redraw = REDRAW_INDEX;
1137       /* No send2hook since this doesn't change the message. */
1138       break;
1139
1140     case OP_COMPOSE_GET_ATTACHMENT:
1141       CHECK_COUNT;
1142       if (menu->tagprefix) {
1143         BODY *top;
1144
1145         for (top = msg->content; top; top = top->next) {
1146           if (top->tagged)
1147             mutt_get_tmp_attachment (top);
1148         }
1149         menu->redraw = REDRAW_FULL;
1150       }
1151       else if (mutt_get_tmp_attachment (idx[menu->current]->content) == 0)
1152         menu->redraw = REDRAW_CURRENT;
1153
1154       /* No send2hook since this doesn't change the message. */
1155       break;
1156
1157     case OP_COMPOSE_RENAME_FILE:
1158       CHECK_COUNT;
1159       strfcpy (fname, idx[menu->current]->content->filename, sizeof (fname));
1160       mutt_pretty_mailbox (fname);
1161       if (mutt_get_field (_("Rename to: "), fname, sizeof (fname), M_FILE)
1162           == 0 && fname[0]) {
1163         if (stat (idx[menu->current]->content->filename, &st) == -1) {
1164           mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
1165           break;
1166         }
1167
1168         mutt_expand_path (fname, sizeof (fname));
1169         if (mutt_rename_file (idx[menu->current]->content->filename, fname))
1170           break;
1171
1172         str_replace (&idx[menu->current]->content->filename, fname);
1173         menu->redraw = REDRAW_CURRENT;
1174
1175         if (idx[menu->current]->content->stamp >= st.st_mtime)
1176           mutt_stamp_attachment (idx[menu->current]->content);
1177
1178       }
1179       mutt_message_hook (NULL, msg, M_SEND2HOOK);
1180       break;
1181
1182     case OP_COMPOSE_NEW_MIME:
1183       {
1184         char type[STRING];
1185         char *p;
1186         int itype;
1187         FILE *fp;
1188
1189         CLEARLINE (LINES - 1);
1190         fname[0] = 0;
1191         if (mutt_get_field (_("New file: "), fname, sizeof (fname), M_FILE)
1192             != 0 || !fname[0])
1193           continue;
1194         mutt_expand_path (fname, sizeof (fname));
1195
1196         /* Call to lookup_mime_type () ?  maybe later */
1197         type[0] = 0;
1198         if (mutt_get_field ("Content-Type: ", type, sizeof (type), 0) != 0
1199             || !type[0])
1200           continue;
1201
1202         if (!(p = strchr (type, '/'))) {
1203           mutt_error _("Content-Type is of the form base/sub");
1204
1205           continue;
1206         }
1207         *p++ = 0;
1208         if ((itype = mutt_check_mime_type (type)) == TYPEOTHER) {
1209           mutt_error (_("Unknown Content-Type %s"), type);
1210           continue;
1211         }
1212         if (idxlen == idxmax) {
1213           mem_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5));
1214           menu->data = idx;
1215         }
1216
1217         idx[idxlen] = (ATTACHPTR *) mem_calloc (1, sizeof (ATTACHPTR));
1218         /* Touch the file */
1219         if (!(fp = safe_fopen (fname, "w"))) {
1220           mutt_error (_("Can't create file %s"), fname);
1221           mem_free (&idx[idxlen]);
1222           continue;
1223         }
1224         fclose (fp);
1225
1226         if ((idx[idxlen]->content = mutt_make_file_attach (fname)) == NULL) {
1227           mutt_error
1228             _("What we have here is a failure to make an attachment");
1229           continue;
1230         }
1231         update_idx (menu, idx, idxlen++);
1232
1233         idx[menu->current]->content->type = itype;
1234         str_replace (&idx[menu->current]->content->subtype, p);
1235         idx[menu->current]->content->unlink = 1;
1236         menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
1237
1238         if (mutt_compose_attachment (idx[menu->current]->content)) {
1239           mutt_update_encoding (idx[menu->current]->content);
1240           menu->redraw = REDRAW_FULL;
1241         }
1242       }
1243       mutt_message_hook (NULL, msg, M_SEND2HOOK);
1244       break;
1245
1246     case OP_COMPOSE_EDIT_MIME:
1247       CHECK_COUNT;
1248       if (mutt_edit_attachment (idx[menu->current]->content)) {
1249         mutt_update_encoding (idx[menu->current]->content);
1250         menu->redraw = REDRAW_FULL;
1251       }
1252       mutt_message_hook (NULL, msg, M_SEND2HOOK);
1253       break;
1254
1255     case OP_VIEW_ATTACH:
1256     case OP_DISPLAY_HEADERS:
1257       CHECK_COUNT;
1258       mutt_attach_display_loop (menu, op, NULL, NULL, NULL, &idx, &idxlen,
1259                                 NULL, 0);
1260       menu->redraw = REDRAW_FULL;
1261       /* no send2hook, since this doesn't modify the message */
1262       break;
1263
1264     case OP_SAVE:
1265       CHECK_COUNT;
1266       mutt_save_attachment_list (NULL, menu->tagprefix,
1267                                  menu->tagprefix ? msg->content : idx[menu->
1268                                                                       current]->
1269                                  content, NULL, menu);
1270       MAYBE_REDRAW (menu->redraw);
1271       /* no send2hook, since this doesn't modify the message */
1272       break;
1273
1274     case OP_PRINT:
1275       CHECK_COUNT;
1276       mutt_print_attachment_list (NULL, menu->tagprefix,
1277                                   menu->tagprefix ? msg->content : idx[menu->
1278                                                                        current]->
1279                                   content);
1280       /* no send2hook, since this doesn't modify the message */
1281       break;
1282
1283     case OP_PIPE:
1284     case OP_FILTER:
1285       CHECK_COUNT;
1286       mutt_pipe_attachment_list (NULL, menu->tagprefix,
1287                                  menu->tagprefix ? msg->content : idx[menu->
1288                                                                       current]->
1289                                  content, op == OP_FILTER);
1290       if (op == OP_FILTER)      /* cte might have changed */
1291         menu->redraw = menu->tagprefix ? REDRAW_FULL : REDRAW_CURRENT;
1292       menu->redraw |= REDRAW_STATUS;
1293       mutt_message_hook (NULL, msg, M_SEND2HOOK);
1294       break;
1295
1296     case OP_EXIT:
1297       if ((i =
1298            query_quadoption (OPT_POSTPONE,
1299                              _("Postpone this message?"))) == M_NO) {
1300         while (idxlen-- > 0) {
1301           /* avoid freeing other attachments */
1302           idx[idxlen]->content->next = NULL;
1303           idx[idxlen]->content->parts = NULL;
1304           if (idx[idxlen]->unowned)
1305             idx[idxlen]->content->unlink = 0;
1306           mutt_free_body (&idx[idxlen]->content);
1307           mem_free (&idx[idxlen]->tree);
1308           mem_free (&idx[idxlen]);
1309         }
1310         mem_free (&idx);
1311         idxlen = 0;
1312         idxmax = 0;
1313         r = -1;
1314         loop = 0;
1315         break;
1316       }
1317       else if (i == -1)
1318         break;                  /* abort */
1319
1320       /* fall through to postpone! */
1321
1322     case OP_COMPOSE_POSTPONE_MESSAGE:
1323
1324       if (check_attachments (idx, idxlen) != 0) {
1325         menu->redraw = REDRAW_FULL;
1326         break;
1327       }
1328
1329       loop = 0;
1330       r = 1;
1331       break;
1332
1333     case OP_COMPOSE_ISPELL:
1334       endwin ();
1335       snprintf (buf, sizeof (buf), "%s -x %s", NONULL (Ispell),
1336                 msg->content->filename);
1337       if (mutt_system (buf) == -1)
1338         mutt_error (_("Error running \"%s\"!"), buf);
1339       else {
1340         mutt_update_encoding (msg->content);
1341         menu->redraw |= REDRAW_STATUS;
1342       }
1343       break;
1344
1345     case OP_COMPOSE_WRITE_MESSAGE:
1346
1347       fname[0] = '\0';
1348       if (Context) {
1349         strfcpy (fname, NONULL (Context->path), sizeof (fname));
1350         mutt_pretty_mailbox (fname);
1351       }
1352       if (idxlen)
1353         msg->content = idx[0]->content;
1354       if (mutt_enter_fname
1355           (_("Write message to mailbox"), fname, sizeof (fname),
1356            &menu->redraw, 1) != -1 && fname[0]) {
1357         mutt_message (_("Writing message to %s ..."), fname);
1358         mutt_expand_path (fname, sizeof (fname));
1359
1360         if (msg->content->next)
1361           msg->content = mutt_make_multipart (msg->content);
1362
1363         if (mutt_write_fcc (NONULL (fname), msg, NULL, 1, NULL) < 0)
1364           msg->content = mutt_remove_multipart (msg->content);
1365         else
1366           mutt_message _("Message written.");
1367       }
1368       break;
1369
1370
1371
1372     case OP_COMPOSE_PGP_MENU:
1373       if (!(WithCrypto & APPLICATION_PGP))
1374         break;
1375       if ((WithCrypto & APPLICATION_SMIME)
1376           && msg->security & APPLICATION_SMIME) {
1377         if (mutt_yesorno (_("S/MIME already selected. Clear & continue ? "),
1378                           M_YES) != M_YES) {
1379           mutt_clear_error ();
1380           break;
1381         }
1382         msg->security = 0;
1383       }
1384       msg->security = crypt_pgp_send_menu (msg, &menu->redraw);
1385       redraw_crypt_lines (msg);
1386       mutt_message_hook (NULL, msg, M_SEND2HOOK);
1387       break;
1388
1389
1390     case OP_FORGET_PASSPHRASE:
1391       crypt_forget_passphrase ();
1392       break;
1393
1394
1395     case OP_COMPOSE_SMIME_MENU:
1396       if (!(WithCrypto & APPLICATION_SMIME))
1397         break;
1398
1399       if ((WithCrypto & APPLICATION_PGP)
1400           && msg->security & APPLICATION_PGP) {
1401         if (mutt_yesorno (_("PGP already selected. Clear & continue ? "),
1402                           M_YES) != M_YES) {
1403           mutt_clear_error ();
1404           break;
1405         }
1406         msg->security = 0;
1407       }
1408       msg->security = crypt_smime_send_menu (msg, &menu->redraw);
1409       redraw_crypt_lines (msg);
1410       mutt_message_hook (NULL, msg, M_SEND2HOOK);
1411       break;
1412
1413
1414 #ifdef MIXMASTER
1415     case OP_COMPOSE_MIX:
1416
1417       mix_make_chain (&msg->chain, &menu->redraw);
1418       mutt_message_hook (NULL, msg, M_SEND2HOOK);
1419       break;
1420 #endif
1421
1422     }
1423
1424     /* Draw formated compose status line */
1425     if (menu->redraw & REDRAW_STATUS) {
1426       compose_status_line (buf, sizeof (buf), menu, NONULL (ComposeFormat));
1427       CLEARLINE (option (OPTSTATUSONTOP) ? 0 : LINES - 2);
1428       SETCOLOR (MT_COLOR_STATUS);
1429       printw ("%-*.*s", COLS, COLS, buf);
1430       SETCOLOR (MT_COLOR_NORMAL);
1431       menu->redraw &= ~REDRAW_STATUS;
1432     }
1433   }
1434
1435   mutt_menuDestroy (&menu);
1436
1437   if (idxlen) {
1438     msg->content = idx[0]->content;
1439     for (i = 0; i < idxlen; i++) {
1440       idx[i]->content->aptr = NULL;
1441       mem_free (&idx[i]);
1442     }
1443   }
1444   else
1445     msg->content = NULL;
1446
1447   mem_free (&idx);
1448
1449   return (r);
1450 }