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