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