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