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