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