LINES - 1 is the status bar, living on stdscr.
[apps/madmutt.git] / compose.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4  * Copyright (C) 2004 g10 Code GmbH
5  *
6  * Parts were written/modified by:
7  * Nico Golde <nico@ngolde.de>
8  *
9  * This file is part of mutt-ng, see http://www.muttng.org/.
10  * It's licensed under the GNU General Public License,
11  * please see the file GPL in the top level source directory.
12  */
13
14 #include <lib-lib/lib-lib.h>
15
16 #include <lib-sys/unix.h>
17 #include <lib-mime/mime.h>
18
19 #include <lib-ui/lib-ui.h>
20 #include <lib-ui/enter.h>
21 #include <lib-ui/menu.h>
22 #include <lib-mx/mx.h>
23
24 #include "mutt.h"
25 #include "alias.h"
26 #include "crypt.h"
27 #include "mutt_idna.h"
28 #include "attach.h"
29 #include "recvattach.h"
30 #include "sort.h"
31 #include "charset.h"
32 #include "buffy.h"
33
34 #define CHECK_COUNT \
35     if (idxlen == 0) {                             \
36         mutt_error _("There are no attachments."); \
37         break;                                     \
38     }
39
40 enum {
41   HDR_FROM = 1,
42   HDR_TO,
43   HDR_CC,
44   HDR_BCC,
45   HDR_SUBJECT,
46   HDR_REPLYTO,
47   HDR_FCC,
48
49   HDR_CRYPT,
50   HDR_CRYPTINFO,
51
52   HDR_ATTACH = (HDR_FCC + 7)
53 };
54
55 #define HDR_XOFFSET     14
56 #define TITLE_FMT       "%14s"        /* Used for Prompts, which are ASCII */
57 #define W               (getmaxx(main_w) - HDR_XOFFSET)
58
59 static const char *Prompts[] = {
60     "From: ",
61     "To: ",
62     "Cc: ",
63     "Bcc: ",
64     "Subject: ",
65     "Reply-To: ",
66     "Fcc: ",
67 };
68
69 static void snd_entry (char *b, ssize_t blen, MUTTMENU * menu, int num) {
70   m_strformat(b, blen, getmaxx(main_w), AttachFormat, mutt_attach_fmt,
71               ((ATTACHPTR **)menu->data)[num], M_FORMAT_STAT_FILE);
72 }
73
74 static void redraw_crypt_lines (HEADER * msg)
75 {
76   int off = 0;
77
78   if (!msg->security)
79     mvwaddstr (main_w, HDR_CRYPT, 0, "    Security: ");
80   else if (msg->security & APPLICATION_SMIME)
81     mvwaddstr (main_w, HDR_CRYPT, 0, "      S/MIME: ");
82   else if (msg->security & APPLICATION_PGP)
83     mvwaddstr (main_w, HDR_CRYPT, 0, "         PGP: ");
84
85   if ((msg->security & (ENCRYPT | SIGN)) == (ENCRYPT | SIGN))
86     waddstr (main_w, _("Sign, Encrypt"));
87   else if (msg->security & ENCRYPT)
88     waddstr (main_w, _("Encrypt"));
89   else if (msg->security & SIGN)
90     waddstr (main_w, _("Sign"));
91   else
92     waddstr (main_w, _("Clear"));
93
94   if ((msg->security & APPLICATION_PGP)
95       && (msg->security & (ENCRYPT | SIGN))) {
96     if ((msg->security & INLINE))
97       waddstr (main_w, _(" (inline)"));
98     else
99       waddstr (main_w, _(" (PGP/MIME)"));
100   }
101   wclrtoeol (main_w);
102
103   wmove (main_w, HDR_CRYPTINFO, 0);
104   wclrtoeol (main_w);
105   if (msg->security & APPLICATION_PGP && msg->security & SIGN)
106     wprintw (main_w, "%s%s", _("     sign as: "),
107             PgpSignAs ? PgpSignAs : _("<default>"));
108
109   if (msg->security & APPLICATION_SMIME && msg->security & SIGN) {
110     wprintw (main_w, "%s%s", _("     sign as: "),
111             SmimeDefaultKey ? SmimeDefaultKey : _("<default>"));
112   }
113
114   if ((msg->security & APPLICATION_SMIME)
115       && (msg->security & ENCRYPT)
116       && SmimeCryptAlg && *SmimeCryptAlg) {
117     mvwprintw (main_w, HDR_CRYPTINFO, 40, "%s%s", _("Encrypt with: "),
118               NONULL (SmimeCryptAlg));
119     off = 20;
120   }
121 }
122
123 static int check_attachments (ATTACHPTR ** idx, short idxlen)
124 {
125   int i, r;
126   struct stat st;
127   char pretty[_POSIX_PATH_MAX], msg[_POSIX_PATH_MAX + STRING];
128
129   for (i = 0; i < idxlen; i++) {
130     m_strcpy(pretty, sizeof(pretty), idx[i]->content->filename);
131     if (stat (idx[i]->content->filename, &st) != 0) {
132       mutt_pretty_mailbox (pretty);
133       mutt_error (_("%s [#%d] no longer exists!"), pretty, i + 1);
134       return -1;
135     }
136
137     if (idx[i]->content->stamp < st.st_mtime) {
138       mutt_pretty_mailbox (pretty);
139       snprintf (msg, sizeof (msg), _("%s [#%d] modified. Update encoding?"),
140                 pretty, i + 1);
141
142       if ((r = mutt_yesorno (msg, M_YES)) == M_YES)
143         mutt_update_encoding (idx[i]->content);
144       else if (r == -1)
145         return -1;
146     }
147   }
148
149   return 0;
150 }
151
152 static void draw_envelope_addr (int line, address_t * addr)
153 {
154   char buf[STRING];
155
156   buf[0] = 0;
157   rfc822_addrcat(buf, sizeof (buf), addr, 1);
158   mvwprintw (main_w, line, 0, TITLE_FMT, Prompts[line - 1]);
159   mutt_paddstr (main_w, W, buf);
160 }
161
162 static void draw_envelope (HEADER * msg, char *fcc)
163 {
164   draw_envelope_addr (HDR_FROM, msg->env->from);
165   draw_envelope_addr (HDR_TO, msg->env->to);
166   draw_envelope_addr (HDR_CC, msg->env->cc);
167   draw_envelope_addr (HDR_BCC, msg->env->bcc);
168   mvwprintw (main_w, HDR_SUBJECT, 0, TITLE_FMT, Prompts[HDR_SUBJECT - 1]);
169   mutt_paddstr (main_w, W, NONULL (msg->env->subject));
170   draw_envelope_addr (HDR_REPLYTO, msg->env->reply_to);
171   mvwprintw (main_w, HDR_FCC, 0, TITLE_FMT, Prompts[HDR_FCC - 1]);
172   mutt_paddstr (main_w, W, fcc);
173
174   redraw_crypt_lines (msg);
175
176   SETCOLOR(main_w, MT_COLOR_STATUS);
177   mvwaddstr (main_w, HDR_ATTACH - 1, 0, _("-- Attachments"));
178   BKGDSET(main_w, MT_COLOR_STATUS);
179   wclrtoeol (main_w);
180
181   BKGDSET(main_w, MT_COLOR_NORMAL);
182   SETCOLOR(main_w, MT_COLOR_NORMAL);
183 }
184
185 static int edit_address_list (int line, address_t ** addr)
186 {
187   char buf[HUGE_STRING] = "";   /* needs to be large for alias expansion */
188   char *err = NULL;
189
190   mutt_addrlist_to_local (*addr);
191   rfc822_addrcat(buf, sizeof (buf), *addr, 0);
192   if (mutt_get_field (Prompts[line - 1], buf, sizeof (buf), M_ALIAS) == 0) {
193     address_list_wipe(addr);
194     *addr = mutt_parse_adrlist (*addr, buf);
195     *addr = mutt_expand_aliases (*addr);
196   }
197
198   if (option (OPTNEEDREDRAW)) {
199     unset_option (OPTNEEDREDRAW);
200     return REDRAW_FULL;
201   }
202
203   if (mutt_addrlist_to_idna (*addr, &err) != 0) {
204     mutt_error (_("Warning: '%s' is a bad IDN."), err);
205     mutt_refresh ();
206     p_delete(&err);
207   }
208
209   /* redraw the expanded list so the user can see the result */
210   buf[0] = 0;
211   rfc822_addrcat(buf, sizeof (buf), *addr, 1);
212   wmove (main_w, line, HDR_XOFFSET);
213   mutt_paddstr (main_w, W, buf);
214
215   return 0;
216 }
217
218 static int delete_attachment (MUTTMENU * menu, short *idxlen, int x)
219 {
220   ATTACHPTR **idx = (ATTACHPTR **) menu->data;
221   int y;
222
223   menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
224
225   if (x == 0 && menu->max == 1) {
226     mutt_error _("You may not delete the only attachment.");
227
228     idx[x]->content->tagged = 0;
229     return -1;
230   }
231
232   for (y = 0; y < *idxlen; y++) {
233     if (idx[y]->content->next == idx[x]->content) {
234       idx[y]->content->next = idx[x]->content->next;
235       break;
236     }
237   }
238
239   idx[x]->content->next = NULL;
240   idx[x]->content->parts = NULL;
241   body_list_wipe(&(idx[x]->content));
242   p_delete(&idx[x]->tree);
243   p_delete(&idx[x]);
244   for (; x < *idxlen - 1; x++)
245     idx[x] = idx[x + 1];
246   menu->max = --(*idxlen);
247
248   return (0);
249 }
250
251 static void update_idx (MUTTMENU * menu, ATTACHPTR ** idx, short idxlen)
252 {
253   idx[idxlen]->level = (idxlen > 0) ? idx[idxlen - 1]->level : 0;
254   if (idxlen)
255     idx[idxlen - 1]->content->next = idx[idxlen]->content;
256   idx[idxlen]->content->aptr = idx[idxlen];
257   menu->current = idxlen++;
258   mutt_update_tree (idx, idxlen);
259   menu->max = idxlen;
260   return;
261 }
262
263
264 /*
265  * cum_attachs_size: Cumulative Attachments Size
266  *
267  * Returns the total number of bytes used by the attachments in the
268  * attachment list _after_ content-transfer-encodings have been
269  * applied.
270  *
271  */
272 static unsigned long cum_attachs_size(MUTTMENU * menu)
273 {
274   ssize_t s;
275   unsigned short i;
276   ATTACHPTR **idx = menu->data;
277   CONTENT *info;
278   BODY *b;
279
280   for (i = 0, s = 0; i < menu->max; i++) {
281     b = idx[i]->content;
282
283     if (!b->content)
284       b->content = mutt_get_content_info (b->filename, b);
285
286     if ((info = b->content)) {
287       switch (b->encoding) {
288       case ENCQUOTEDPRINTABLE:
289         s += 3 * (info->lobin + info->hibin) + info->ascii + info->crlf;
290         break;
291       case ENCBASE64:
292         s += (4 * (info->lobin + info->hibin + info->ascii + info->crlf)) / 3;
293         break;
294       default:
295         s += info->lobin + info->hibin + info->ascii + info->crlf;
296         break;
297       }
298     }
299   }
300
301   return s;
302 }
303
304 /*
305  * compose_format_str()
306  *
307  * %a = total number of attachments
308  * %h = hostname  [option]
309  * %l = approx. length of current message (in bytes)
310  * %v = Mutt version
311  *
312  * This function is similar to status_format_str().  Look at that function for
313  * help when modifying this function.
314  */
315 static void compose_status_line (char *buf, ssize_t buflen, MUTTMENU * menu,
316                                  const char *p);
317
318 static const char *compose_format_str (char *buf, ssize_t buflen, char op,
319                                        const char *src, const char *prefix,
320                                        const char *ifstr,
321                                        const char *elstr,
322                                        anytype data, format_flag flags)
323 {
324   char fmt[STRING], tmp[STRING];
325   int optional = (flags & M_FORMAT_OPTIONAL);
326   MUTTMENU *menu = data.ptr;
327
328   *buf = 0;
329   switch (op) {
330   case 'a':                    /* total number of attachments */
331     snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
332     snprintf (buf, buflen, fmt, menu->max);
333     break;
334
335   case 'h':                    /* hostname */
336     snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
337     snprintf (buf, buflen, fmt, NONULL(mod_core.shorthost));
338     break;
339
340   case 'l':                    /* approx length of current message in bytes */
341     snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
342     mutt_pretty_size (tmp, sizeof (tmp), menu ? cum_attachs_size (menu) : 0);
343     snprintf (buf, buflen, fmt, tmp);
344     break;
345
346   case 'v':
347     m_strcpy(buf, buflen, mutt_make_version());
348     break;
349
350   case 0:
351     *buf = 0;
352     return (src);
353
354   default:
355     *buf = 0;
356     break;
357   }
358
359   if (flags & M_FORMAT_OPTIONAL)
360     compose_status_line(buf, buflen, menu, optional ? ifstr : elstr);
361
362   return (src);
363 }
364
365 static void compose_status_line (char *buf, ssize_t buflen, MUTTMENU * menu,
366                                  const char *p)
367 {
368     m_strformat(buf, buflen, getmaxx(main_w), p, compose_format_str, menu, 0);
369 }
370
371 /* return values:
372  *
373  * 1    message should be postponed
374  * 0    normal exit
375  * -1   abort message
376  */
377 int mutt_compose_menu (HEADER * msg,    /* structure for new message */
378                        char *fcc,       /* where to save a copy of the message */
379                        ssize_t fcclen,
380                        HEADER * cur __attribute__ ((unused)))
381 {                               /* current message */
382   char buf[LONG_STRING];
383   char fname[_POSIX_PATH_MAX];
384   MUTTMENU *menu;
385   ATTACHPTR **idx = NULL;
386   short idxlen = 0;
387   short idxmax = 0;
388   int i, closed = 0;
389   int r = -1;                   /* return value */
390   int op = 0;
391   int loop = 1;
392   int fccSet = 0;               /* has the user edited the Fcc: field ? */
393   CONTEXT *ctx = NULL, *this = NULL;
394
395   /* Sort, SortAux could be changed in mutt_index_menu() */
396   int oldSort, oldSortAux;
397   struct stat st;
398
399   mutt_attach_init (msg->content);
400   idx = mutt_gen_attach_list (msg->content, -1, idx, &idxlen, &idxmax, 0, 1);
401
402   menu = mutt_new_menu ();
403   menu->menu = MENU_COMPOSE;
404   menu->offset = HDR_ATTACH;
405   menu->max = idxlen;
406   menu->make_entry = snd_entry;
407   menu->tag = mutt_tag_attach;
408   menu->data = idx;
409
410   while (loop) {
411     switch (op = mutt_menuLoop (menu)) {
412     case OP_REDRAW:
413       draw_envelope (msg, fcc);
414       menu->offset = HDR_ATTACH;
415       menu->pagelen = LINES - HDR_ATTACH - 2;
416       break;
417     case OP_COMPOSE_EDIT_FROM:
418       menu->redraw = edit_address_list (HDR_FROM, &msg->env->from);
419       mutt_message_hook (NULL, msg, M_SEND2HOOK);
420       break;
421     case OP_COMPOSE_EDIT_TO:
422       menu->redraw = edit_address_list (HDR_TO, &msg->env->to);
423       mutt_message_hook (NULL, msg, M_SEND2HOOK);
424       break;
425     case OP_COMPOSE_EDIT_BCC:
426       menu->redraw = edit_address_list (HDR_BCC, &msg->env->bcc);
427       mutt_message_hook (NULL, msg, M_SEND2HOOK);
428       break;
429     case OP_COMPOSE_EDIT_CC:
430       menu->redraw = edit_address_list (HDR_CC, &msg->env->cc);
431       mutt_message_hook (NULL, msg, M_SEND2HOOK);
432       break;
433     case OP_COMPOSE_EDIT_SUBJECT:
434       if (msg->env->subject)
435         m_strcpy(buf, sizeof(buf), msg->env->subject);
436       else
437         buf[0] = 0;
438       if (mutt_get_field ("Subject: ", buf, sizeof (buf), 0) == 0) {
439         m_strreplace(&msg->env->subject, buf);
440         wmove (main_w, HDR_SUBJECT, HDR_XOFFSET);
441         wclrtoeol (main_w);
442         if (msg->env->subject)
443           mutt_paddstr (main_w, W, msg->env->subject);
444       }
445       mutt_message_hook (NULL, msg, M_SEND2HOOK);
446       break;
447     case OP_COMPOSE_EDIT_REPLY_TO:
448       menu->redraw = edit_address_list (HDR_REPLYTO, &msg->env->reply_to);
449       mutt_message_hook (NULL, msg, M_SEND2HOOK);
450       break;
451     case OP_COMPOSE_EDIT_FCC:
452       m_strcpy(buf, sizeof(buf), fcc);
453       if (mutt_get_field ("Fcc: ", buf, sizeof (buf), M_FILE | M_CLEAR) == 0) {
454         m_strcpy(fcc, _POSIX_PATH_MAX, buf);
455         mutt_pretty_mailbox (fcc);
456         wmove (main_w, HDR_FCC, HDR_XOFFSET);
457         mutt_paddstr (main_w, W, fcc);
458         fccSet = 1;
459       }
460       MAYBE_REDRAW (menu->redraw);
461       mutt_message_hook (NULL, msg, M_SEND2HOOK);
462       break;
463     case OP_COMPOSE_EDIT_MESSAGE:
464       if (!option (OPTEDITHDRS)) {
465         mutt_edit_file(msg->content->filename);
466         mutt_update_encoding (msg->content);
467         menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
468         mutt_message_hook (NULL, msg, M_SEND2HOOK);
469         break;
470       }
471       /* fall through */
472     case OP_COMPOSE_EDIT_HEADERS:
473       if ((op == OP_COMPOSE_EDIT_HEADERS ||
474            (op == OP_COMPOSE_EDIT_MESSAGE && option (OPTEDITHDRS)))) {
475         const char *tag = NULL;
476         char *err = NULL;
477
478         mutt_env_to_local (msg->env);
479         mutt_edit_headers(msg->content->filename, msg, fcc, fcclen);
480         if (mutt_env_to_idna (msg->env, &tag, &err)) {
481           mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err);
482           p_delete(&err);
483         }
484       }
485       mutt_update_encoding (msg->content);
486
487       /* attachments may have been added */
488       if (idxlen && idx[idxlen - 1]->content->next) {
489         for (i = 0; i < idxlen; i++)
490           p_delete(&idx[i]);
491         idxlen = 0;
492         idx =
493           mutt_gen_attach_list (msg->content, -1, idx, &idxlen, &idxmax, 0,
494                                 1);
495         menu->data = idx;
496         menu->max = idxlen;
497       }
498
499       menu->redraw = REDRAW_FULL;
500       mutt_message_hook (NULL, msg, M_SEND2HOOK);
501       break; 
502
503     case OP_COMPOSE_ATTACH_FILE:
504       {
505         const char *prompt;
506         char **files;
507         int error, numfiles;
508
509         fname[0] = 0;
510         prompt = _("Attach file");
511         numfiles = 0;
512         files = NULL;
513
514         if (_mutt_enter_fname
515             (prompt, fname, sizeof (fname), &menu->redraw, 0, 1, &files,
516              &numfiles) == -1 || *fname == '\0')
517           break;
518
519         if (idxlen + numfiles >= idxmax) {
520           p_realloc(&idx, idxmax += 5 + numfiles);
521           menu->data = idx;
522         }
523
524         error = 0;
525         if (numfiles > 1)
526           mutt_message _("Attaching selected files...");
527
528         for (i = 0; i < numfiles; i++) {
529           char *att = files[i];
530
531           idx[idxlen] = p_new(ATTACHPTR, 1);
532           idx[idxlen]->unowned = 1;
533           idx[idxlen]->content = mutt_make_file_attach (att);
534           if (idx[idxlen]->content != NULL)
535             update_idx (menu, idx, idxlen++);
536           else {
537             error = 1;
538             mutt_error (_("Unable to attach %s!"), att);
539             p_delete(&idx[idxlen]);
540           }
541         }
542
543         p_delete(&files);
544         if (!error)
545           mutt_clear_error ();
546
547         menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
548       }
549       mutt_message_hook (NULL, msg, M_SEND2HOOK);
550       break;
551
552     case OP_COMPOSE_ATTACH_MESSAGE:
553       {
554         const char *prompt;
555         HEADER *h;
556
557         fname[0] = 0;
558         prompt = _("Open mailbox to attach message from");
559
560
561         if (Context)
562           {
563             m_strcpy(fname, sizeof(fname), NONULL(Context->path));
564             mutt_pretty_mailbox (fname);
565           }
566
567         if (mutt_enter_fname (prompt, fname, sizeof (fname), &menu->redraw, 1)
568             == -1 || !fname[0])
569           break;
570
571         mutt_expand_path (fname, sizeof (fname));
572         if (mx_get_magic (fname) != M_IMAP && mx_get_magic (fname) != M_POP) {
573               /* check to make sure the file exists and is readable */
574           if (access (fname, R_OK) == -1) {
575             mutt_perror (fname);
576             break;
577           }
578         }
579
580         menu->redraw = REDRAW_FULL;
581
582         ctx = mx_open_mailbox (fname, M_READONLY, NULL);
583         if (ctx == NULL) {
584           mutt_perror (fname);
585           break;
586         }
587
588         if (!ctx->msgcount) {
589           mx_close_mailbox (ctx, NULL);
590           p_delete(&ctx);
591           mutt_error _("No messages in that folder.");
592           break;
593         }
594
595         this = Context;         /* remember current folder and sort methods */
596         oldSort = Sort;
597         oldSortAux = SortAux;
598
599         Context = ctx;
600         set_option (OPTATTACHMSG);
601         mutt_message _("Tag the messages you want to attach!");
602
603         closed = mutt_index_menu ();
604         unset_option (OPTATTACHMSG);
605
606         if (!Context) {
607           /* go back to the folder we started from */
608           Context = this;
609           /* Restore old $sort and $sort_aux */
610           Sort = oldSort;
611           SortAux = oldSortAux;
612           menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
613           break;
614         }
615
616         if (idxlen + Context->tagged >= idxmax) {
617           p_realloc(&idx, idxmax += 5 + Context->tagged);
618           menu->data = idx;
619         }
620
621         for (i = 0; i < Context->msgcount; i++) {
622           h = Context->hdrs[i];
623           if (h->tagged) {
624             idx[idxlen] = p_new(ATTACHPTR, 1);
625             idx[idxlen]->content = mutt_make_message_attach (Context, h, 1);
626             if (idx[idxlen]->content != NULL)
627               update_idx (menu, idx, idxlen++);
628             else {
629               mutt_error _("Unable to attach!");
630               p_delete(&idx[idxlen]);
631             }
632           }
633         }
634         menu->redraw |= REDRAW_FULL;
635
636         if (closed == OP_QUIT)
637           mx_close_mailbox (Context, NULL);
638         else
639           mx_fastclose_mailbox (Context);
640         p_delete(&Context);
641
642         /* go back to the folder we started from */
643         Context = this;
644         /* Restore old $sort and $sort_aux */
645         Sort = oldSort;
646         SortAux = oldSortAux;
647         mutt_message_hook (NULL, msg, M_SEND2HOOK);
648       }
649       break;
650
651     case OP_DELETE:
652       CHECK_COUNT;
653       if (idx[menu->current]->unowned)
654         idx[menu->current]->content->unlink = 0;
655       if (delete_attachment (menu, &idxlen, menu->current) == -1)
656         break;
657       mutt_update_tree (idx, idxlen);
658       if (idxlen) {
659         if (menu->current > idxlen - 1)
660           menu->current = idxlen - 1;
661       }
662       else
663         menu->current = 0;
664
665       if (menu->current == 0)
666         msg->content = idx[0]->content;
667
668       menu->redraw |= REDRAW_STATUS;
669       mutt_message_hook (NULL, msg, M_SEND2HOOK);
670       break;
671
672 #define CURRENT idx[menu->current]->content
673
674     case OP_COMPOSE_TOGGLE_RECODE:
675       {
676         CHECK_COUNT;
677         if (!mutt_is_text_part (CURRENT)) {
678           mutt_error (_("Recoding only affects text attachments."));
679           break;
680         }
681         CURRENT->noconv = !CURRENT->noconv;
682         if (CURRENT->noconv)
683           mutt_message (_("The current attachment won't be converted."));
684         else
685           mutt_message (_("The current attachment will be converted."));
686         menu->redraw = REDRAW_CURRENT;
687         mutt_message_hook (NULL, msg, M_SEND2HOOK);
688         break;
689       }
690 #undef CURRENT
691
692     case OP_COMPOSE_EDIT_DESCRIPTION:
693       CHECK_COUNT;
694       m_strcpy(buf, sizeof(buf),
695                NONULL(idx[menu->current]->content->description));
696       /* header names should not be translated */
697       if (mutt_get_field ("Description: ", buf, sizeof (buf), 0) == 0) {
698         m_strreplace(&idx[menu->current]->content->description, buf);
699         menu->redraw = REDRAW_CURRENT;
700       }
701       mutt_message_hook (NULL, msg, M_SEND2HOOK);
702       break;
703
704     case OP_COMPOSE_UPDATE_ENCODING:
705       CHECK_COUNT;
706       if (menu->tagprefix) {
707         BODY *top;
708
709         for (top = msg->content; top; top = top->next) {
710           if (top->tagged)
711             mutt_update_encoding (top);
712         }
713         menu->redraw = REDRAW_FULL;
714       }
715       else {
716         mutt_update_encoding (idx[menu->current]->content);
717         menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
718       }
719       mutt_message_hook (NULL, msg, M_SEND2HOOK);
720       break;
721
722     case OP_COMPOSE_TOGGLE_DISPOSITION:
723       /* toggle the content-disposition between inline/attachment */
724       idx[menu->current]->content->disposition =
725         (idx[menu->current]->content->disposition ==
726          DISPINLINE) ? DISPATTACH : DISPINLINE;
727       menu->redraw = REDRAW_CURRENT;
728       break;
729
730     case OP_EDIT_TYPE:
731       CHECK_COUNT;
732       {
733         mutt_edit_content_type (NULL, idx[menu->current]->content, NULL);
734
735         /* this may have been a change to text/something */
736         mutt_update_encoding (idx[menu->current]->content);
737
738         menu->redraw = REDRAW_CURRENT;
739       }
740       mutt_message_hook (NULL, msg, M_SEND2HOOK);
741       break;
742
743     case OP_COMPOSE_EDIT_ENCODING:
744       CHECK_COUNT;
745       m_strcpy(buf, sizeof(buf),
746                ENCODING(idx[menu->current]->content->encoding));
747       if (mutt_get_field ("Content-Transfer-Encoding: ", buf,
748                           sizeof (buf), 0) == 0 && buf[0]) {
749         if ((i = mutt_check_encoding (buf)) != ENCOTHER && i != ENCUUENCODED) {
750           idx[menu->current]->content->encoding = i;
751           menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
752           mutt_clear_error ();
753         }
754         else
755           mutt_error _("Invalid encoding.");
756       }
757       mutt_message_hook (NULL, msg, M_SEND2HOOK);
758       break;
759
760     case OP_COMPOSE_SEND_MESSAGE:
761
762       /* Note: We don't invoke send2-hook here, since we want to leave
763        * users an opportunity to change settings from the ":" prompt.
764        */
765
766       if (check_attachments (idx, idxlen) != 0) {
767         menu->redraw = REDRAW_FULL;
768         break;
769       }
770
771       if (!fccSet && *fcc) {
772         if ((i = query_quadoption (OPT_COPY,
773                                    _("Save a copy of this message?"))) == -1)
774           break;
775         else if (i == M_NO)
776           *fcc = 0;
777       }
778
779       loop = 0;
780       r = 0;
781       break;
782
783     case OP_COMPOSE_EDIT_FILE:
784       CHECK_COUNT;
785       mutt_edit_file(idx[menu->current]->content->filename);
786       mutt_update_encoding (idx[menu->current]->content);
787       menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
788       mutt_message_hook (NULL, msg, M_SEND2HOOK);
789       break;
790
791     case OP_COMPOSE_TOGGLE_UNLINK:
792       CHECK_COUNT;
793       idx[menu->current]->content->unlink =
794         !idx[menu->current]->content->unlink;
795
796       menu->redraw = REDRAW_INDEX;
797       /* No send2hook since this doesn't change the message. */
798       break;
799
800     case OP_COMPOSE_GET_ATTACHMENT:
801       CHECK_COUNT;
802       if (menu->tagprefix) {
803         BODY *top;
804
805         for (top = msg->content; top; top = top->next) {
806           if (top->tagged)
807             mutt_get_tmp_attachment (top);
808         }
809         menu->redraw = REDRAW_FULL;
810       }
811       else if (mutt_get_tmp_attachment (idx[menu->current]->content) == 0)
812         menu->redraw = REDRAW_CURRENT;
813
814       /* No send2hook since this doesn't change the message. */
815       break;
816
817     case OP_COMPOSE_RENAME_FILE:
818       CHECK_COUNT;
819       m_strcpy(fname, sizeof(fname), idx[menu->current]->content->filename);
820       mutt_pretty_mailbox (fname);
821       if (mutt_get_field (_("Rename to: "), fname, sizeof (fname), M_FILE)
822           == 0 && fname[0]) {
823         if (stat (idx[menu->current]->content->filename, &st) == -1) {
824           mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
825           break;
826         }
827
828         mutt_expand_path (fname, sizeof (fname));
829         if (mutt_rename_file (idx[menu->current]->content->filename, fname))
830           break;
831
832         m_strreplace(&idx[menu->current]->content->filename, fname);
833         menu->redraw = REDRAW_CURRENT;
834
835         if (idx[menu->current]->content->stamp >= st.st_mtime)
836           mutt_stamp_attachment (idx[menu->current]->content);
837
838       }
839       mutt_message_hook (NULL, msg, M_SEND2HOOK);
840       break;
841
842     case OP_COMPOSE_NEW_MIME:
843       {
844         char type[STRING];
845         char *p;
846         int itype;
847         FILE *fp;
848
849         CLEARLINE(stdscr, LINES - 1);
850         fname[0] = 0;
851         if (mutt_get_field (_("New file: "), fname, sizeof (fname), M_FILE)
852             != 0 || !fname[0])
853           continue;
854         mutt_expand_path (fname, sizeof (fname));
855
856         /* Call to lookup_mime_type () ?  maybe later */
857         type[0] = 0;
858         if (mutt_get_field ("Content-Type: ", type, sizeof (type), 0) != 0
859             || !type[0])
860           continue;
861
862         if (!(p = strchr (type, '/'))) {
863           mutt_error _("Content-Type is of the form base/sub");
864
865           continue;
866         }
867         *p++ = 0;
868         if ((itype = mutt_check_mime_type (type)) == TYPEOTHER) {
869           mutt_error (_("Unknown Content-Type %s"), type);
870           continue;
871         }
872         if (idxlen == idxmax) {
873           p_realloc(&idx, idxmax += 5);
874           menu->data = idx;
875         }
876
877         idx[idxlen] = p_new(ATTACHPTR, 1);
878         /* Touch the file */
879         if (!(fp = safe_fopen (fname, "w"))) {
880           mutt_error (_("Can't create file %s"), fname);
881           p_delete(&idx[idxlen]);
882           continue;
883         }
884         m_fclose(&fp);
885
886         if ((idx[idxlen]->content = mutt_make_file_attach (fname)) == NULL) {
887           mutt_error
888             _("What we have here is a failure to make an attachment");
889           continue;
890         }
891         update_idx (menu, idx, idxlen++);
892
893         idx[menu->current]->content->type = itype;
894         m_strreplace(&idx[menu->current]->content->subtype, p);
895         idx[menu->current]->content->unlink = 1;
896         menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
897
898         if (mutt_compose_attachment (idx[menu->current]->content)) {
899           mutt_update_encoding (idx[menu->current]->content);
900           menu->redraw = REDRAW_FULL;
901         }
902       }
903       mutt_message_hook (NULL, msg, M_SEND2HOOK);
904       break;
905
906     case OP_COMPOSE_EDIT_MIME:
907       CHECK_COUNT;
908       if (mutt_edit_attachment (idx[menu->current]->content)) {
909         mutt_update_encoding (idx[menu->current]->content);
910         menu->redraw = REDRAW_FULL;
911       }
912       mutt_message_hook (NULL, msg, M_SEND2HOOK);
913       break;
914
915     case OP_VIEW_ATTACH:
916     case OP_DISPLAY_HEADERS:
917       CHECK_COUNT;
918       mutt_attach_display_loop (menu, op, NULL, NULL, NULL, &idx, &idxlen,
919                                 NULL, 0);
920       menu->redraw = REDRAW_FULL;
921       /* no send2hook, since this doesn't modify the message */
922       break;
923
924     case OP_SAVE:
925       CHECK_COUNT;
926       mutt_save_attachment_list (NULL, menu->tagprefix,
927                                  menu->tagprefix ? msg->content : idx[menu->
928                                                                       current]->
929                                  content, NULL, menu);
930       MAYBE_REDRAW (menu->redraw);
931       /* no send2hook, since this doesn't modify the message */
932       break;
933
934     case OP_PRINT:
935       CHECK_COUNT;
936       mutt_print_attachment_list (NULL, menu->tagprefix,
937                                   menu->tagprefix ? msg->content : idx[menu->
938                                                                        current]->
939                                   content);
940       /* no send2hook, since this doesn't modify the message */
941       break;
942
943     case OP_PIPE:
944     case OP_FILTER:
945       CHECK_COUNT;
946       mutt_pipe_attachment_list (NULL, menu->tagprefix,
947                                  menu->tagprefix ? msg->content : idx[menu->
948                                                                       current]->
949                                  content, op == OP_FILTER);
950       if (op == OP_FILTER)      /* cte might have changed */
951         menu->redraw = menu->tagprefix ? REDRAW_FULL : REDRAW_CURRENT;
952       menu->redraw |= REDRAW_STATUS;
953       mutt_message_hook (NULL, msg, M_SEND2HOOK);
954       break;
955
956     case OP_EXIT:
957       if ((i =
958            query_quadoption (OPT_POSTPONE,
959                              _("Postpone this message?"))) == M_NO) {
960         while (idxlen-- > 0) {
961           /* avoid freeing other attachments */
962           idx[idxlen]->content->next = NULL;
963           idx[idxlen]->content->parts = NULL;
964           if (idx[idxlen]->unowned)
965             idx[idxlen]->content->unlink = 0;
966           body_list_wipe(&idx[idxlen]->content);
967           p_delete(&idx[idxlen]->tree);
968           p_delete(&idx[idxlen]);
969         }
970         p_delete(&idx);
971         idxlen = 0;
972         idxmax = 0;
973         r = -1;
974         loop = 0;
975         break;
976       }
977       else if (i == -1)
978         break;                  /* abort */
979
980       /* fall through to postpone! */
981
982     case OP_COMPOSE_POSTPONE_MESSAGE:
983
984       if (check_attachments (idx, idxlen) != 0) {
985         menu->redraw = REDRAW_FULL;
986         break;
987       }
988
989       loop = 0;
990       r = 1;
991       break;
992
993     case OP_COMPOSE_WRITE_MESSAGE:
994       fname[0] = '\0';
995       if (Context) {
996         m_strcpy(fname, sizeof(fname), NONULL(Context->path));
997         mutt_pretty_mailbox (fname);
998       }
999       if (idxlen)
1000         msg->content = idx[0]->content;
1001       if (mutt_enter_fname
1002           (_("Write message to mailbox"), fname, sizeof (fname),
1003            &menu->redraw, 1) != -1 && fname[0]) {
1004         mutt_message (_("Writing message to %s ..."), fname);
1005         mutt_expand_path (fname, sizeof (fname));
1006
1007         if (msg->content->next)
1008           msg->content = mutt_make_multipart (msg->content);
1009
1010         if (mutt_write_fcc (fname, msg, NULL, 1, NULL) < 0)
1011           msg->content = mutt_remove_multipart (msg->content);
1012         else
1013           mutt_message _("Message written.");
1014       }
1015       break;
1016
1017     case OP_COMPOSE_PGP_MENU:
1018       if (msg->security & APPLICATION_SMIME) {
1019         if (mutt_yesorno (_("S/MIME already selected. Clear & continue ? "),
1020                           M_YES) != M_YES) {
1021           mutt_clear_error ();
1022           break;
1023         }
1024         msg->security = 0;
1025       }
1026       msg->security = crypt_send_menu (msg, &menu->redraw, 0);
1027       redraw_crypt_lines (msg);
1028       mutt_message_hook (NULL, msg, M_SEND2HOOK);
1029       break;
1030
1031     case OP_COMPOSE_SMIME_MENU:
1032       if (msg->security & APPLICATION_PGP) {
1033         if (mutt_yesorno (_("PGP already selected. Clear & continue ? "),
1034                           M_YES) != M_YES) {
1035           mutt_clear_error ();
1036           break;
1037         }
1038         msg->security = 0;
1039       }
1040       msg->security = crypt_send_menu(msg, &menu->redraw, 1);
1041       redraw_crypt_lines (msg);
1042       mutt_message_hook (NULL, msg, M_SEND2HOOK);
1043       break;
1044     }
1045
1046     /* Draw formated compose status line */
1047     if (menu->redraw & REDRAW_STATUS) {
1048       compose_status_line (buf, sizeof (buf), menu, NONULL (ComposeFormat));
1049       CLEARLINE(main_w, 0);
1050       SETCOLOR(main_w, MT_COLOR_STATUS);
1051       mvwprintw (main_w, 0, 0, "%-*.*s", getmaxx(main_w), getmaxx(main_w), buf);
1052       SETCOLOR(main_w, MT_COLOR_NORMAL);
1053       menu->redraw &= ~REDRAW_STATUS;
1054     }
1055   }
1056
1057   mutt_menuDestroy (&menu);
1058
1059   if (idxlen) {
1060     msg->content = idx[0]->content;
1061     for (i = 0; i < idxlen; i++) {
1062       idx[i]->content->aptr = NULL;
1063       p_delete(&idx[i]);
1064     }
1065   } else {
1066     msg->content = NULL;
1067   }
1068
1069   p_delete(&idx);
1070
1071   return (r);
1072 }